问题
I am currently having some troubles when using boost enable_shared_from_this
and multiple inheritance.
The scenario can be described as follows:
Class
A
implements some functionality and should inherit fromenable_shared_from_this
Class
B
implements another functionality and should inherit fromenable_shared_from_this
Class
D
inherits functionalities fromA
andB
(class D : public A, public B {}
)When using some class
B
functionality from classD
I got an exception (bad_weak_ptr
)To inherit
enable_shared_from_this
from classD
is not an option for me
I am not sure about how to solve this.
Oh, I am using Visual C++ 2010.
回答1:
Expanding on Potatoswatter's solution, if you can change A and B to use a something slightly different than enable_shared_from_this. The code uses the standard library versions, but the boost implementation should be similar. Explanation below
#include <memory>
template<typename T>
struct enable_shared_from_this_virtual;
class enable_shared_from_this_virtual_base : public std::enable_shared_from_this<enable_shared_from_this_virtual_base>
{
typedef std::enable_shared_from_this<enable_shared_from_this_virtual_base> base_type;
template<typename T>
friend struct enable_shared_from_this_virtual;
std::shared_ptr<enable_shared_from_this_virtual_base> shared_from_this()
{
return base_type::shared_from_this();
}
std::shared_ptr<enable_shared_from_this_virtual_base const> shared_from_this() const
{
return base_type::shared_from_this();
}
};
template<typename T>
struct enable_shared_from_this_virtual: virtual enable_shared_from_this_virtual_base
{
typedef enable_shared_from_this_virtual_base base_type;
public:
std::shared_ptr<T> shared_from_this()
{
std::shared_ptr<T> result(base_type::shared_from_this(), static_cast<T*>(this));
return result;
}
std::shared_ptr<T const> shared_from_this() const
{
std::shared_ptr<T const> result(base_type::shared_from_this(), static_cast<T const*>(this));
return result;
}
};
So, the intent is that struct A
would inherit publicly from enable_shared_from_this_virtual<A>
and struct B
would inherit publicly from enable_shared_from_this_virtual<B>
. Since they both share the same common virtual object, there is only one std::enable_shared_from_this
in the hierarchy.
When you call either classes shared_from_this
, it performs a cast from enable_shared_from_this_virtual<T>*
to T*, and uses the aliasing constructor of shared_ptr so that the new shared_ptr points to T* and shares ownership with the single shared pointer.
The use of the friend at the top is to prevent anyone from accessing the shared_from_this() members of the virtual base directly. They have to go through the ones provided by enable_shared_from_this_virtual<T>
.
An example:
#include <iostream>
struct A : public enable_shared_from_this_virtual<A>
{
void foo()
{
shared_from_this()->baz();
}
void baz()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct B : public enable_shared_from_this_virtual<B>
{
void bar()
{
shared_from_this()->baz();
}
void baz()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct D: A, B {};
int main()
{
std::shared_ptr<D> d(new D);
d->foo();
d->bar();
return 0;
}
回答2:
A shared_ptr
is an observer to an invisible container object, or "control block." This container is always on the heap, never on the stack, and you can't get direct handle on it. Although shared_ptr
and weak_ptr
provide the only means of observation, there is still a hidden layer which owns the object. It is that hidden layer which populates enable_shared_from_this
.
Multiple inheritance from enable_shared_from_this
is not allowed because all the enable_shared_from_this
bases would need to be initialized individually, but the control block has no way of getting a list of all base subobjects of a given type. All it can get is an ambiguous base error.
The only solution I see is to add a virtual
base class of A
and B
which inherits from enable_shared_from_this
. You might designate a particular class for this purpose:
struct shared_from_this_virtual_base
: std::enable_shared_from_this< shared_from_this_virtual_base >
{};
struct shared_from_this_base : virtual shared_from_this_virtual_base {};
struct A : shared_from_this_base { … };
struct B : shared_from_this_base { … };
But, there's another problem: you can't down-cast from a virtual
base because it's ambiguous whether A
or B
contains the shared_from_this_virtual_base
. To recover an A*
or B*
you would have to add those pointers to some kind of registry structure within shared_from_this_virtual_base
. This would probably be populated by adding another CRTP pattern to shared_from_this_base
. It's a bit more footwork than usual for C++ but I don't see any conceptual shortcut.
EDIT: Ah, the simplest way to get the downcast is to put a void*
member in shared_from_this_virtual_base
and then cast that to D*
in whatever client code has knowledge of D
. Quite workable, if not perfectly elegant. Or boost::any
could provide a safer alternative.
回答3:
The standard is a bit vague about how shared_from_this
is supposed to be implemented, but all implementations seem to agree, because Boost provides a reference implementation.
When you create boost::shared_ptr<D> myD(new D())
the shared_ptr
constructor checks if there is an unambiguous conversion from D*
to enable_shared_from_this<X>*
for some X
(which implies that D
has a base class of type enable_shared_from_this<X>
). If the conversion works then a weak_ptr<X>
in the base class will be set to refer to the newly-created shared_ptr
.
In your code there are two possible conversions, to enable_shared_from_this<A>
and enable_shared_from_this<B>
, which is ambiguous, so no weak_ptr
gets set to refer to myD
. That means if a member function of B
later calls shared_from_this()
it will get a bad_weak_ptr
exception, because its enable_shared_from_this<B>
member was never set.
来源:https://stackoverflow.com/questions/14939190/boost-shared-from-this-and-multiple-inheritance