Thursday, 12 September 2013

Object storing a non-owning reference that must be informed before the reference is destructed

Object storing a non-owning reference that must be informed before the
reference is destructed

I have a class following this pattern:
class Foo
{
public:
// Create a Foo whose value is absolute
Foo(int x) : other_(0), a_(x) {}
// Create a Foo whose value is relative to another Foo
Foo(Foo * other, int dx) : other_(other), a_(dx) {}
// Get the value
double x() const
{
if(other_)
return other_->x() + a_;
else
return a_;
}
private:
Foo * other_;
int a_;
};
The Foo objects are all owned by a Bar:
class Bar
{
public:
~Bar() { for(int i=0; i<foos_.size(); i++) delete foos_[i]; }
private:
vector<Foo*> foos_;
};
Of course, this is a simplified example to get the idea. I have a
guarantee that there are no loop of Foos, and that linked Foos all belong
to the same instance of Bar. So far, so good. To do things the C++11 way,
I would use vector< unique_ptr<Foo> > foos_; in Bar, and pass
foos_[i].get() as potential argument of a Foo constructor.
There is the deal:
This a GUI application, and the user can interactively delete some Foo at
will. The expected behaviour is that if foo1 is deleted, and foo2 is
relative to foo1, then foo2 becomes now "absolute":
void Foo::convertToAbsolute() { a_ += other_->x(); other_ = 0; }
void usageScenario()
{
Foo * foo1 = new Foo(42);
Foo * foo2 = new Foo(foo1, 42);
// Here, foo1->x() = 42 and foo2->x() = 84
foo1->setX(10);
// Here, foo1->x() = 10 and foo2->x() = 52
delete foo1;
// Here, foo2->x() = 52
}
How would you handle this situation? The simplest raw pointer approach I
can think of is to have a two-way link instead of a one-way, so the Foo
are aware of who is relative to them:
class Foo { /* ... */ vector<Foo*> areRelativeToMe_; };
Foo::~Foo()
{
for(int i=0; i<areRelativeToMe_.size(); i++)
areRelativeToMe_[i]->convertToAbsolute();
}
Is there a way using standard C++11 smart pointers to avoid having this
two-way dependency (well, the dependency is hidden instead), and then
avoid explicitely calling areRelativeToMe_[i]->convertToAbsolute(); in the
destructor of Foo? I was thinking about weak_ptr, something in the spirit
of:
class Foo { /* ... */ weak_ptr<Foo> other_; };
double Foo::x() const
{
if(other_.isExpired())
convertToAbsolute();
// ...
}
But the issue is that convertToAbsolute() needs the relative Foo to still
exist. So I need a non-owning smart-pointer that can tell "this reference
is logically expired", but actually extends the lifetime of the referenced
object, until it is not needed.
It could be seen either like a weak_ptr extending the lifetime until it is
not shared with any other weak_ptr:
class Foo { /* ... */ extended_weak_ptr<Foo> other_; };
double Foo::x() const
{
if(other_.isExpired())
{
convertToAbsolute();
other_.reset(); // now the object is destructed, unless other
// foos still have to release it
}
// ...
}
Or like a shared_ptr with different level of ownership:
class Bar { /* ... */ vector< multilevel_shared_ptr<Foo> foos_; };
class Foo { /* ... */ multilevel_shared_ptr<Foo> other_; };
void Bar::createFoos()
{
// Bar owns the Foo* with the highest level of ownership "Level1"
// Creating an absolute Foo
foos_.push_back( multilevel_unique_ptr<Foo>(new Foo(42), Level1) );
// Creating a relative Foo
foos_.push_back( multilevel_unique_ptr<Foo>(new Foo(foos_[0],7),
Level1) );
}
Foo::Foo(const multilevel_unique_ptr<Foo> & other, int dx) :
other_( other, Level2 ),
// Foo owns the Foo* with the lowest level of ownership "Level2"
a_(dx)
{
}
double Foo::x() const
{
if(other_.noLevel1Owner()) // returns true if not shared
// with any Level1 owner
{
convertToAbsolute();
other_.reset(); // now the object is destructed, unless
// shared with other Level2 owners
}
// ...
}
Any thoughts?

No comments:

Post a Comment