How to enable_shared_from_this of both parent and derived
Sorry, but there isn't.
The problem is that shared_ptr<foo>
and shared_ptr<bar1>
are different types. I don't understand everything that's going on under the hood, but I think that when the constructor returns and is assigned to a shared_ptr<foo>
, the internal weak_ptr<bar1>
sees that nothing is pointing to it (because only a shared_ptr<bar1>
would increment the counter) and resets itself. When you call bar1::shared_from_this
in get_callback
, you get the exception because the internal weak_ptr
isn't pointing to anything.
Essentially, enable_shared_from_this
only seems to work transparently from a single class in a hierarchy. If you try implementing it manually, the problem should become obvious.
shared_from_this with derived class
It has nothing to do with inheritance. Call method
this way instead will work: std::make_shared<foo_derived>()->method();
cppreference std::enable_shared_from_this::shared_from_this
It is permitted to call shared_from_this only on a previously shared
object, i.e. on an object managed by std::shared_ptr. Otherwise the
behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the
shared_ptr constructor from a default-constructed weak_this) (since
C++17).
Use of enable_shared_from_this with multiple inheritance
Indeed you are doing it wrong. If you have simple inheritance, just inherit from enable_shared_from this
in the base class, and derived class get it for free. (of course you'll need to downcast the result)
If you have multiple inheritance (like it seems), you must use the trick described here and also here :
/* Trick to allow multiple inheritance of objects
* inheriting shared_from_this.
* cf. https://stackoverflow.com/a/12793989/587407
*/
/* First a common base class
* of course, one should always virtually inherit from it.
*/
class MultipleInheritableEnableSharedFromThis: public std::enable_shared_from_this<MultipleInheritableEnableSharedFromThis>
{
public:
virtual ~MultipleInheritableEnableSharedFromThis()
{}
};
template <class T>
class inheritable_enable_shared_from_this : virtual public MultipleInheritableEnableSharedFromThis
{
public:
std::shared_ptr<T> shared_from_this() {
return std::dynamic_pointer_cast<T>(MultipleInheritableEnableSharedFromThis::shared_from_this());
}
/* Utility method to easily downcast.
* Useful when a child doesn't inherit directly from enable_shared_from_this
* but wants to use the feature.
*/
template <class Down>
std::shared_ptr<Down> downcasted_shared_from_this() {
return std::dynamic_pointer_cast<Down>(MultipleInheritableEnableSharedFromThis::shared_from_this());
}
};
Then your code becomes :
class A: public inheritable_enable_shared_from_this<A>
{
public:
void foo1()
{
auto ptr = shared_from_this();
}
};
class B: public inheritable_enable_shared_from_this<B>
{
public:
void foo2()
{
auto ptr = shared_from_this();
}
};
class C: public inheritable_enable_shared_from_this<C>
{
public:
void foo3()
{
auto ptr = shared_from_this();
}
};
class D: public A, public B, public C
{
public:
void foo()
{
auto ptr = A::downcasted_shared_from_this<D>();
}
};
Using std::enable_shared_from_this in base classes
In order not to expose shared_from_this
you can make it protected
(visible whithin whole hierarchy) or private
(visible only inside the class) explicitly:
#include <memory>
class Foo : public std::enable_shared_from_this<Foo>
{
private:
using std::enable_shared_from_this<Foo>::shared_from_this;
};
Can't std::shared_from_this be inherited by its derived classes?
It is inherited:
// enable_shared_from_this example
#include <iostream>
#include <memory>
struct C : std::enable_shared_from_this<C> {};
struct D : public C {};
int main () {
std::shared_ptr<D> foo;
foo = std::make_shared<D>();
std::shared_ptr<C> bar = foo->shared_from_this();
return 0;
}
You define the shared_from_this
in the class C
: so the object returns std::shated_ptr<C>
. No surprise. You still may downcast it to std::shared_ptr<D>
with std::dynamic_pointer_cast
.
Using enable_shared_from_this in polymorphic inheritance with virtual destructor
Some minor points:
- You could move the shared pointer into the lambda to avoid an atomic increment and decrement
- No need to use a dynamic pointer cast since you know for sure the dynamic type (plus you don't check the result is not empty anyway!)
void MethodHandlerB::operator()(void* data){
auto thisPtr = std::static_pointer_cast<MethodHandlerB>(this->shared_from_this());
putLamdaToSomeGlobalEventThing([thisPtr = std::move(thisPtr)](){
thisPtr->doSomething();
});
}
- Alternatively, you could use separate captures for
this
and the shared pointer, which avoids the cast altogether:
void MethodHandlerB::operator()(void* data){
putLamdaToSomeGlobalEventThing([this, thisPtr = shared_from_this()](){
doSomething();
});
}
Edit: as one of the comments points out, if you don't use shared_from_this()
directly on the base class, you're better off just deriving from enable_shared_from_this
in the derived classes. You can do this because C++ supports multiple inheritence.
class MethodHandlerBase {
public:
virtual void operator()(void* data) = 0;
virtual ~MethodHandlerBase(){}
};
class MethodHandlerA:
public MethodHandlerBase,
public std::enable_shared_from_this<MethodHandlerA>
{
private:
MethodHandlerACallback cb;
public:
MethodHandlerA(MethodHandlerACallback cb): cb(cb){}
virtual void operator()(void* data);
};
void MethodHandlerA::operator()(void* data){
putLamdaToSomeGlobalEventThing([self = shared_from_this()](){
self->doSomething();
});
}
Double inheritance of enable_shared_from_this
Yes, as per bad weak pointer when base and derived class both inherit from boost::enable_shared_from_this the solution is to use virtual inheritance. Here's an implementation for the C++11 standard shared_ptr
(not Boost):
#include <memory>
struct virtual_enable_shared_from_this_base:
std::enable_shared_from_this<virtual_enable_shared_from_this_base> {
virtual ~virtual_enable_shared_from_this_base() {}
};
template<typename T>
struct virtual_enable_shared_from_this:
virtual virtual_enable_shared_from_this_base {
std::shared_ptr<T> shared_from_this() {
return std::dynamic_pointer_cast<T>(
virtual_enable_shared_from_this_base::shared_from_this());
}
};
struct A: virtual_enable_shared_from_this<A> {};
struct B: virtual_enable_shared_from_this<B> {};
struct Z: A, B { };
int main() {
std::shared_ptr<Z> z = std::make_shared<Z>();
std::shared_ptr<B> b = z->B::shared_from_this();
}
This isn't part of the default implementation, probably because of the overhead of virtual inheritance.
Is it OK to derive from std::enable_shared_from_this and an abstract base class?
Yes, it's absolutely fine.
shared_ptr
has a templated constructor that takes a "convertible" pointer. And unless theshared_ptr
constructor knows it is taking aWidget
it doesn't know it derives fromenable_shared_from_this
and it can't store aweak_ptr
.
Exactly right.
I just wonder if this behaviour is documented.
In the current standard enable_shared_from_this
is very poorly specified, but see P0033 for the new and improved specification of enable_shared_from_this
which will be in C++17. As well as answering the "what happens if you do it twice?" question, the revised wording specifies exactly how the enable_shared_from_this
base class is used, and how the weak_ptr
member is initialized. That part of the new wording is just standardising existing practice, i.e. it is just a more accurate description of what real implementations already do. (The answer to the "what happens if you do it twice?" question deviates from what implementations previously did, but for good reasons, and that isn't relevant to your question anyway).
The new spec clarifies that your original example is entirely well-defined and correct.
The current standard says that the modified version in your updated question has undefined behaviour when you call shared_from_this()
, due to violating the precondition that there is shared_ptr
that owns the pointer-to-derived (because you create a shared_ptr
that owns the pointer-to-base). However, that precondition is not sufficient to ensure sensible semantics, as explained in the paper. The revised wording makes your modified version also well-defined (i.e. no undefined behaviour) but the weak_ptr
in the base class will not share ownership with the shared_ptr<IWidget>
and so shared_from_this()
will throw an exception (which is what you observe from your implementation).
Must enable_shared_from_this be the first base class?
When
~A()
and~B()
run, can I be sure that the storage whereC
lived
is still present?
Of course! It would be hard to use a base class that tries to free its own memory (the memory where it resides). I'm not sure it's even formally legal.
Implementations don't do that: when a shared_ptr<T>
is destructed or reset, the reference count (RC) for the shared ownership of T
is decremented (atomically); if it reached 0 in the decrement, then destruction/deletion of T
is started.
Then the weak-owners-or-T-exists count is decremented (atomically), as T
no longer exists: we need to know if we are the last entity standing interested in the control block; if the decrement gave a non zero result, it means some weak_ptr
exist that share (could be 1 share, or 100%) ownership of control block, and they are now responsible for the deallocation.
Either way, atomic decrement will at some point end up with a zero value, for the last co-owner.
Here there are no threads, no non-determinism, and obviously the last weak_ptr<T>
was destroyed during destruction of C
. (The unwritten assumption in your question being that no other weak_ptr<T>
was kept.)
Destruction always happen in that exact order. The control block is used for destruction, as no shared_ptr<T>
knows (in general) which (potentially non virtual) destructor of (potentially different) most derived class to call. (The control block also knows not to deallocate memory on shared count reaching zero for make_shared
.)
The only practical variation between implementations seems to be about the fine details of memory fences and avoiding some atomic operations in common cases.
Related Topics
Making a C++ Class a Monitor (In the Concurrent Sense)
How to Use Multiple Versions of Gcc
How to Clone Object in C++? or Is There Another Solution
What Should a C++ Getter Return
What Are Use Cases for Structured Bindings
How to Increment an Iterator by Just Adding a Number
Can You Have a Triple Minus Signs in C Programming? What Does It Mean
How to Detect Whether Windows Is Shutting Down or Restarting
Gcc -Wuninitialized/-Wmaybe-Uninitialized Issues
Lto, Devirtualization, and Virtual Tables
How Does C++ Link Template Instances
Cost of Throwing C++0X Exceptions