Introduction
Smart Pointers in the Boost C++ libraries and now in C++11 offer a facility to retrieve a shared_ptr
from the this
pointer in a member function. This is done via the enable_shared_from_this
template class. A good example of the necessity of having a shared_ptr
from this
is when you want to call a function func
inside a member function, and you need to pass this
in argument to the function func
. By passing this
as a raw pointer, you would break the rule of not mixing shared_ptr
and raw pointer to the same memory block, negating the shared_ptr
usefulness. Using enable_shared_from_this
, you can send a shared_ptr
from this
to the function func
, ensuring only shared_ptr
to the object allocation space are used in your program.
The enable_shared_from_this
template class has a member weak_ptr
to this
and a method, shared_from_this
that returns a shared_ptr
to this
by locking that weak_ptr
. The way the weak_ptr
is initialized is in the shared_ptr
constructor, by using a clever trick to statically determines if the object being wrapped inherits from enable_shared_from_this
.
The problem with this is that it assumes that there is only one weak_ptr
to initialize. What if enable_shared_from_this
appears more than one time in the inheritance tree, like the following:
In a situation like this, either one of the enable_shared_from_this
' weak_ptr
or none of them will be initialized, depending on the compiler (yes it is true, different compilers behave differently for this!). So when an object of type Derived
tries to access its shared_from_this
through Base
(e.g. in a method of Base
) and/or in a method of Derived
, it won't be initialized to this
even though a shared_ptr
for the class was instantiated. Typically, one assumes that shared_from_this
is always initialized as long as Derived
is instantiated via shared_ptr
, but in that situation, it is not the case and any access to it will cause a segmentation fault.
This article proposes a solution to initialize all the enable_shared_from_this
' weak_ptr
in an inheritance tree.
Background
If you have a crash after trying to indirect a shared_from_this()
, and you are sure that all the objects of your class are pointed by shared_ptr
, then verify the inheritance tree of the class. If you have more than one enable_shared_from_this
in the tree, then there is your problem.
This happened to me because I have implemented a SharedObserver
pattern (Observer pattern that uses shared_ptr
). In the pattern, I send the Subject
to the Observer
via a shared_from_this()
in the notify
method of the Subject
. Therefore, SharedSubject
needed to inherit from enable_shared_from_this
. One of my concrete Subject
s in my program also needed to use shared_from_this()
, thus inheriting a second time from enable_shared_from_this
and there goes the crash and the investigation that lead me to this solution.
Using the Code
First, I needed to modify slightly the enable_shared_from_this
, so I created a class EnableSharedFromThis
. Therefore, make your class inherit from EnableSharedFromThis
instead of enable_shared_from_this
.
class Base : public EnableSharedFromThis< Base >
{
public:
Base(int iValue) : mValue(iValue) {}
void BaseFunc()
{
FuncBase(EnableSharedFromThis< Base >::SharedFromThis());
}
void PrintValue()
{
std::cout << "Value = " << mValue << std::endl;
}
private:
int mValue;
};
class Derived : public Base, public EnableSharedFromThis< Derived >
{
public:
Derived(int iValue) : Base(iValue) {}
void DerivedFunc()
{
FuncDerived(EnableSharedFromThis< Derived >::SharedFromThis());
}
};
Here is the implementation of FuncBase
and FuncDerived
, two free functions that receive respectively a shared_ptr
of Base
and one of Derived
. They are called inside a member function of Base
and Derived
respectively. Both member functions pass the corresponding SharedFromThis
as argument to the free functions.
void FuncBase(std::shared_ptr< Base > iBase)
{
iBase->PrintValue();
}
void FuncDerived(std::shared_ptr< Derived > iDerived)
{
iDerived->PrintValue();
}
Then I created a SmartPtrBuilder
with a static
template method CreateSharedPtr
that you can call, specifying a variable number of template argument, one for each class that inherits from EnableSharedFromThis
that needs to be initialized.
std::shared_ptr< Derived > wDerived = SmartPtrBuilder::CreateSharedPtr
< Derived, Base >(new Derived(123));
Then you can use wDerived
normally and all its internal weak_ptrs
will be initialized.
wDerived->BaseFunc();
wDerived->DerivedFunc();
Points of Interest
Factory Method
There is a good way not to force users of your classes to instantiate them using SmartPtrBuilder::CreateSharedPtr
and thus not making these users responsible to correctly specify the correct types to pass as template argument everywhere. You can simply provided them with a factory method that hides all those details, like the following:
class Derived : public Base, public EnableSharedFromThis< Derived >
{
public:
static std::shared_ptr< Derived > Create(int iValue)
{
return SmartPtrBuilder::CreateSharedPtr< Derived, Base >(new Derived(iValue));
}
void DerivedFunc()
{
FuncDerived(EnableSharedFromThis< Derived >::SharedFromThis());
}
private:
Derived(int iValue) : Base(iValue) {}
};
And instantiate Derived
like the following:
std::shared_ptr< Derived > wDerived = Derived::Create(123);
Other Thoughts
Another good solution for this whole enable_shared_from_this
problem exists, but involves dynamic_cast
. It is possible to create a polymorphic base class for the template class enable_shared_from_this
. The weak_ptr
is actually inside the polymorphic base class and when shared_from_this method
is called, a dynamic_cast
of the weak_ptr
of the polymorphic base class is done, ensuring a proper type for the returned shared_ptr
. However, since the solution involves dynamic_cast
, the performance impact might be not negligible. Also RTTI would be required and it is not always possible to use it, like in some RTOS environment, making this solution not universal. By the way, static_cast
instead of dynamic_cast
in that situation would not always compile, e.g.
if you use virtual inheritance ; this is why we are forced to use dynamic_cast
as a generic solution.
As food for thought, if a way of exploring or inspecting an inheritance tree statically (at compile time) were possible, then a solution where you do not have to provide all the classes that inherit from enable_shared_from_this
upon construction of the shared_ptr
would be possible. But even if it is theoretically possible to implement this feature in a C++ compiler, I don't think it is done in the current version of the standard (C++11 at the moment of writing this article).
Also, I have created a variadic template version of the SmartPtrBuilder
, if you want it, please ask in the comment below or contact me.
History
Version 1.0
The zip file contains the original EnableSharedFromThis
template class, the SmartPtrBuilder
and a usage example as a VS2010 solution file (you can use VS2010 Express freely available here). For GCC, you just have to build the example and run it like this:
$ g++ -std=c++0x main.cpp -o esft
$ ./esft