Suppose I have a collection of Foo
objects, and each Foo
owns one or more Bar
objects. A specific Foo
or Bar
can be deleted by a user of my interface; when a Foo
is deleted, all the Bar
s it owns are deleted too. So far, giving each Foo
a collection of unique_ptr<Bar>
is all I need to automatically manage this model. However, I have an additional requirement: once a Foo
is left without any Bar
, it should also be deleted.
Of course, I could just write code that explicitly takes care of that, but I wonder if there's a more idiomatic way to achieve the same thing. That sounds an awful lot like a shared_ptr
, but not quite...
Since deleting all Bar
s from a Foo
should delete the Foo
, it may indeed sound like you need a handful of shared_ptr
s from Bar
s to their Foo
.
However, that model would place the lifetime of the Foo
in the hands of its Bar
s: you wouldn't be able to directly delete the Foo
, instead you'd have to hunt down all of its Bar
s and delete these.
Foo
would keep Bar*
s instead of unique_ptr<Bar>
s, since it cannot die before its Bar
s. But then you have to give ownership of the Bar
s to someone...
You might end up with yet another object that holds the collection of unique_ptr<Bar>
s corresponding to each of the Foo
s, but then you have to keep all of this in sync as Bar
s come and go. That's the same kind of bookkeeping you're trying to avoid, but a lot bigger, more complicated, and more brittle as a result, with memory leaks and rogue pointers as failure cases.
So instead of all of this, I suggest you stick with your first unique_ptr
-powered idea. A first go at an implementation might look like this:
struct Foo {
private:
friend void remove(std::unique_ptr<Foo> &foo, Bar const *bar);
// Removes the bar from this Foo.
// Returns true iff the Foo is now empty and should be deleted.
bool remove(Bar const *bar) {
auto i = std::find_if(begin(_bars), end(_bars), [&](auto const &p) {
return p.get() == bar;
});
assert(i != end(_bars));
_bars.erase(i);
return _bars.empty();
}
std::vector<std::unique_ptr<Bar>> _bars;
};
// Removes the bar from the Foo.
// Deletes the Foo if it becomes empty.
void remove(std::unique_ptr<Foo> &foo, Bar const *bar) {
if(foo->remove(bar))
foo.reset();
}