I do not have a lot of expertise in shared pointers, but yes, this seems a very appropriate use of weak_ptr
.
In this case, you are just annoyed that:
- You would like to use
InputConsumer
s directly as members ofModelController
s, since its a trivial ownership relation. - You are forced to use a
shared_ptr
to make it work with aweak_ptr
.
I think this is solved by using a shared_ptr
as an alias of a member object. According to C++.com:
Additionally, shared_ptr objects can share ownership over a pointer while at the same time pointing to another object. This ability is known as aliasing (see constructors), and is commonly used to point to member objects while owning the object they belong to.
I never did it myself, but this seems adapted to your situation:
- Have
InputConsumer
s members ofModelController
s - Have an alias
shared_ptr
for each of them - Reference them in
InputSender
usingweak_ptr
s
EDIT
Here is a complete minimal working example:
#include <iostream>
#include <memory>
using namespace std;
// A class to try our smart pointers on
struct Foo
{
Foo() { cout << "constructing Foo\n"; }
~Foo() { cout << "destructing Foo\n"; }
};
// A class that owns some Foo as members
struct Owner
{
// The actual members
Foo foo1;
Foo foo2;
// A fake shared pointer whose purpose is:
// 1) to be of type shared_ptr<>
// 2) to have the same lifetime as foo1 and foo2
shared_ptr<Owner> self;
// A fake deleter that actually deletes nothing
struct Deleter
{
void operator() (Owner *) { cout << "pretend to delete Owner\n"; }
};
Owner() : self(this, Deleter()) { cout << "constructing Owner\n"; }
~Owner() { cout << "destructing Owner\n"; }
};
// A class that holds a reference to a Foo
struct Observer
{
// A reference to a Foo, as a weak pointer
weak_ptr<Foo> foo_ptr;
Observer(const shared_ptr<Foo> & foo_ptr) : foo_ptr(foo_ptr)
{
cout << "constructing Observer\n";
}
~Observer() { cout << "destructing Observer\n"; }
void check()
{
if(foo_ptr.expired())
cout << "foo expired\n";
else
cout << "foo still exists\n";
}
};
int main()
{
// Create Owner, and hence foo1 and foo2
Owner * owner = new Owner;
// Create an observer, passing an alias of &(owner->foo1) to ctor
Observer observer(shared_ptr<Foo>(owner->self, &(owner->foo1)));
// Try to access owner->foo1 from observer
observer.check();
delete owner;
observer.check();
return 0;
}
It prints:
constructing Foo
constructing Foo
constructing Owner
constructing Observer
foo still exists
destructing Owner
pretend to delete Owner
destructing Foo
destructing Foo
foo expired
destructing Observer
The tricky part is to be able to create a weak_ptr
to owner->foo1
(would be the same for foo2
). To do that, we first need a shared_ptr
that is an alias of owner->foo1
. This can only be done by:
shared_ptr<Foo> alias(other_shared_ptr, &(owner->foo1));
where other_shared_ptr
is a shared_ptr<T>
whose lifetime is at least as long as the one of owner->foo1
. To achieve this, using a shared_ptr<T>
which is also a member of owner
is a good idea, since it ensures the lifetime would be the same. Finally, we need a valid non-null pointer to give to it, and since we don't want to create anything on the heap, we must use an existing object. this
is a good candidate, since we know it is valid and get destroyed only after its members are destroyed. Hence our other_shared_ptr
is for instance:
shared_ptr<Owner> self(this);
However, this means that when self
gets out of scope, i.e. during the destruction of owner
, it will call delete this
. We do not want this deletion to occur, otherwise this
would be deleted twice (which is undefined behaviour, in practice a segfault). Hence we also provide to the constructor of self
a Deleter that actually doesn't delete anything.
The rest of the code should be self-explanatory with the comments.