在具有虚析构函数的多态继承中使用 enable_shared_from_this
Using enable_shared_from_this in polymorphic inheritance with virtual destructor
我有以下 class 结构来管理具有不同原型的回调:
class MethodHandlerBase: public std::enable_shared_from_this<MethodHandlerBase>{
public:
virtual void operator()(void* data) = 0;
virtual ~MethodHandlerBase(){}
};
class MethodHandlerA: public MethodHandlerBase{
private:
MethodHandlerACallback cb;
public:
MethodHandlerA(MethodHandlerACallback cb): cb(cb){}
virtual void operator()(void* data);
};
class MethodHandlerB: public MethodHandlerBase{
private:
MethodHandlerBCallback cb;
public:
MethodHandlerB(MethodHandlerBCallback cb): cb(cb){}
virtual void operator()(void* data);
};
在某些情况下,MethodHandlerA
或 MethodHandlerB
可能会在传递给其他地方的 lambda 表达式中使用 this
(包含在 shared_ptr 中),因此我需要确定它在需要时被正确删除。因此我将 std::enable_shared_from_this<MethodHandlerBase>
继承添加到基础 class.
但我读到您通常不能通过继承使用 std::enable_shared_from_this
(除了使用模板,它实际上不再是真正的继承)。据我了解,这是由于可能错误地破坏了实例。在这种情况下,我假设我的代码可以正常工作,因为它使用了虚拟析构函数(无论如何都需要)。
那么我的理论是否正确,或者关于 std::enable_shared_from_this
继承还有其他我不明白的地方?
编辑:
添加一个简短的例子来说明我打算如何使用它:
从class里面:
void MethodHandlerB::operator()(void* data){
std::shared_ptr<MethodHandlerB> thisPtr = std::dynamic_pointer_cast<MethodHandlerB>(this->shared_from_this());
putLamdaToSomeGlobalEventThing([thisPtr](){
thisPtr->doSomething();
});
}
从外面
std::vector<MethodHandlerBase> vec{std::make_shared<MethodHandlerB>()};
一些小问题:
- 您可以将共享指针移动到 lambda 中以避免原子递增和递减
- 无需使用动态指针转换,因为您肯定知道动态类型(而且您不检查结果是否为空!)
void MethodHandlerB::operator()(void* data){
auto thisPtr = std::static_pointer_cast<MethodHandlerB>(this->shared_from_this());
putLamdaToSomeGlobalEventThing([thisPtr = std::move(thisPtr)](){
thisPtr->doSomething();
});
}
- 或者,您可以对
this
和共享指针使用单独的捕获,这样可以完全避免转换:
void MethodHandlerB::operator()(void* data){
putLamdaToSomeGlobalEventThing([this, thisPtr = shared_from_this()](){
doSomething();
});
}
编辑:正如其中一条评论所指出的,如果您不直接在基础 class 上使用 shared_from_this()
,您最好从 enable_shared_from_this
派生派生的 classes。你可以这样做,因为C++支持多重继承。
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();
});
}
可以做个小帮手class
template <class Base, class Derived>
struct enable_shared : public Base
{
std::shared_ptr<Derived> shared_from_this()
{
return std::static_pointer_cast<Derived>(
Base::shared_from_this());
};
};
现在您可以在所有这些 class 中自由使用 shared_from_this
。它将 return 正确的类型:
class Base : public std::enable_shared_from_this<Base> ...;
class Derived : public enable_shared<Base, Derived> ...;
class MoreDerived : public enable_shared<Derived, MoreDerived> ...;
顺便说一下,如果您使用 std::make_shared
,那么 不需要 虚拟析构函数,因为共享指针是使用正确的删除器创建的,用于大多数派生类型。无论如何定义一个可能是个好主意,只是为了安全大小。 (也许不是。)
我有以下 class 结构来管理具有不同原型的回调:
class MethodHandlerBase: public std::enable_shared_from_this<MethodHandlerBase>{
public:
virtual void operator()(void* data) = 0;
virtual ~MethodHandlerBase(){}
};
class MethodHandlerA: public MethodHandlerBase{
private:
MethodHandlerACallback cb;
public:
MethodHandlerA(MethodHandlerACallback cb): cb(cb){}
virtual void operator()(void* data);
};
class MethodHandlerB: public MethodHandlerBase{
private:
MethodHandlerBCallback cb;
public:
MethodHandlerB(MethodHandlerBCallback cb): cb(cb){}
virtual void operator()(void* data);
};
在某些情况下,MethodHandlerA
或 MethodHandlerB
可能会在传递给其他地方的 lambda 表达式中使用 this
(包含在 shared_ptr 中),因此我需要确定它在需要时被正确删除。因此我将 std::enable_shared_from_this<MethodHandlerBase>
继承添加到基础 class.
但我读到您通常不能通过继承使用 std::enable_shared_from_this
(除了使用模板,它实际上不再是真正的继承)。据我了解,这是由于可能错误地破坏了实例。在这种情况下,我假设我的代码可以正常工作,因为它使用了虚拟析构函数(无论如何都需要)。
那么我的理论是否正确,或者关于 std::enable_shared_from_this
继承还有其他我不明白的地方?
编辑:
添加一个简短的例子来说明我打算如何使用它:
从class里面:
void MethodHandlerB::operator()(void* data){
std::shared_ptr<MethodHandlerB> thisPtr = std::dynamic_pointer_cast<MethodHandlerB>(this->shared_from_this());
putLamdaToSomeGlobalEventThing([thisPtr](){
thisPtr->doSomething();
});
}
从外面
std::vector<MethodHandlerBase> vec{std::make_shared<MethodHandlerB>()};
一些小问题:
- 您可以将共享指针移动到 lambda 中以避免原子递增和递减
- 无需使用动态指针转换,因为您肯定知道动态类型(而且您不检查结果是否为空!)
void MethodHandlerB::operator()(void* data){
auto thisPtr = std::static_pointer_cast<MethodHandlerB>(this->shared_from_this());
putLamdaToSomeGlobalEventThing([thisPtr = std::move(thisPtr)](){
thisPtr->doSomething();
});
}
- 或者,您可以对
this
和共享指针使用单独的捕获,这样可以完全避免转换:
void MethodHandlerB::operator()(void* data){
putLamdaToSomeGlobalEventThing([this, thisPtr = shared_from_this()](){
doSomething();
});
}
编辑:正如其中一条评论所指出的,如果您不直接在基础 class 上使用 shared_from_this()
,您最好从 enable_shared_from_this
派生派生的 classes。你可以这样做,因为C++支持多重继承。
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();
});
}
可以做个小帮手class
template <class Base, class Derived>
struct enable_shared : public Base
{
std::shared_ptr<Derived> shared_from_this()
{
return std::static_pointer_cast<Derived>(
Base::shared_from_this());
};
};
现在您可以在所有这些 class 中自由使用 shared_from_this
。它将 return 正确的类型:
class Base : public std::enable_shared_from_this<Base> ...;
class Derived : public enable_shared<Base, Derived> ...;
class MoreDerived : public enable_shared<Derived, MoreDerived> ...;
顺便说一下,如果您使用 std::make_shared
,那么 不需要 虚拟析构函数,因为共享指针是使用正确的删除器创建的,用于大多数派生类型。无论如何定义一个可能是个好主意,只是为了安全大小。 (也许不是。)