I have to register an object in a container upon its creation. Without smart pointers I'd use something like this:
a_class::a_class() { register_somewhere(this); }
With smart pointers I should use shared_from_this
but I can't use that in the constructor.
Is there a clean way to solve this problem? What would you do in a similar situation? I'm thinking about introducing an init
method to call just after creation and put everything in a factory function like this:
boost::shared_ptr<a_class> create_a() { boost::shared_ptr<a_class> ptr(new a_class); ptr->init(); return ptr; }
Is it fine or there is a standard procedure to follow in such cases?
EDIT: Actually my case is more complex. I have 2 object which shall maintain pointers each other. So the truth is I'm not "registering" but creating another object (let's say b_class
) which requires this
as a parameter. b_class
receives this
as a weak pointer and stores it.
I'm adding this because since you are giving me design advices (which are very appreciated) at least you can know what I'm doing:
a_class::a_class() { b = new b_class(this); }
In my program a_class
is an entity and b_class
is one of the concrete classes representing the state (in the constructor it's just the starting state). a_class
needs a pointer to the current state and b_class
needs to manipulate the entity.
a_class
is responsible for creating and destroying b_class instances and thus maintains a shared_ptr to them but b_class
need to manipulate a_class
and thus maintains a weak pointer. a_class
instance "survives" b_class
instances.
Do you suggest to avoid using smart pointers in this case?
By deriving your class from the class template enable_shared_from_this , you inherit a method shared_from_this that returns a shared_ptr instance to this . Note(2) you cannot call enable_shared_from_this inside the constructor.
shared_from_this. returns a shared_ptr which shares ownership of *this. (public member function)
a_class
is responsible for creating and destroyingb_class
instances
...
a_class
instance "survives"b_class
instances.
Given these two facts, there should be no danger that a b_class
instance can attempt to access an a_class
instance after the a_class
instance has been destroyed as the a_class
instance is responsible for destroying the b_class
instances.
b_class
can just hold a pointer to it's associated a_class
instance. A raw pointer doesn't express any ownership which is appropriate for this case.
In this example it doesn't matter how the a_class
is created, dynamically, part of a aggregated object, etc. Whatever creates a_class
manages its lifetime just as a_class
manages the lifetime of the b_class
which it instantiates.
E.g.
class a_class; class b_class { public: b_class( a_class* a_ ) : a( a_ ) {} private: a_class* a; }; class a_class { public: a_class() : b( new b_class(this) ) {} private: boost::shared_ptr<b_class> b; };
Note, in this toy example there is no need for a shared_ptr
, an object member would work just as well (assuming that you don't copy your entity class).
class a_class { public: a_class() : b( this ) {} private: b_class b; };
If you absolutely need a shared_ptr during construction, it's best to have an 'init' function. In fact, this is the only decent approach I can think of. You should probably have a special function that creates objects of this type, to ensure init()
is called, if you choose this path.
However, depending on what you're registering for, it may be a better idea to give whatever object you're registering with a plain pointer to the object in the constructor, rather than a shared_ptr. Then in the destructor, you can just unregister the object from the manager.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With