4

Let's say I have an object Foo that uses another object Bar.

Many different Foo could use the same Bar.

When no Foo references a Bar, this Bar is useless and should be destroyed.

Clearly, that's a perfect use case for a shared pointer :

class Foo
{
  Foo() { m_bar = std::make_shared<Bar>(); };
  Foo(const Foo& other) { m_bar = other.m_bar};
  std::shared_ptr<Bar> m_bar;
}

class Bar
{
  Bar(){};
}


Foo myfunc()
{
  Foo foo1;         //bar created
  Foo foo2(foo1);
  //do thing with foo1 or foo2
  return foo2;
}

void main()
{
  Foo foo = myfunc();
}                 //bar destroyed

As expected, the only Bar object that has been created will be destroyed when the last Foo object is destroyed, meaning at the end of my main in my example.


But now, I would like to keep track of all my Bars and have an access to them : I want a BarList, vector of existing Bar.

I may create the vector (like with a foo factory that also fills the vector), but I have problem with maintaining this list and especially removing from it the Bar that have been destroyed.

Obviously I can't have a vector of shared pointer, because then use count never drops to 0.

I could store a weak pointer (or a raw?) but then I have another problem: after some times, my vector will be full of destroyed object.

Given that my Bar are not aware of the BarList, how could I be sure that when a Foo is destroyed, if this triggers an Bar destruction, then this bar will be removed from the BarList ?

Note that my Foos are aware of the BarList (they have a link to it). But the only solution I have for now is a ugly use_count hack : on foo destructor, if bar use_count is 1 (i.e. hold only by this current Foo), then remove it from the list.

What would be the good design for that?

1 Answer 1

6

Your approach using weak pointers is fine, I see mainly two options for completing it:

  • live with the BarList containing a certain number of destroyed objects over some period, but add some kind of "cleanup" or garbage collection process to it which removes any unneeded weak pointer from the vector from time to time.

  • make use of the Observer pattern, where Bar allows observers to register for calls to Bar's destructor. This allows the owner of Barlist to subscribe to this event and cleanup the vector whenever a Bar object is destroyed.

6
  • Callbacks are hard to do with smart pointers. To easy to either lose a reference, or create a loop. Do not use option 2.
    – Basilevs
    Commented Jul 24 at 23:17
  • @Basilevs: feel free to write a better answer (or a constructive comment).
    – Doc Brown
    Commented Jul 25 at 4:56
  • @Basilevs an observer seemed like a good solution to me, why are callbacks hard with smart pointer ?
    – sayanel
    Commented Jul 25 at 9:47
  • @sayanel callback has to make sure it does not hold any strong references, to avoid reference loops that would prevent shared_ptr disposal.
    – Basilevs
    Commented Jul 25 at 10:45
  • If you wrap a node based container (list or set) then iteration could check for dead objects and remove them as it finds them. There are other benefits of wrapping, such as having the iterator hold a strong pointer while it points to an element, so it can always dereference
    – Caleth
    Commented Jul 26 at 8:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.