c++在没有虚拟析构函数的多态性中共享ptr
c++ shared_ptr in polymorphism without virtual destructor
典型的工厂设计模式要求基类声明虚拟析构函数,但使用shared_ptr
实际上可以避免这种情况。
#include <iostream>
#include <memory>
#include <cstdio>
using namespace std;
class Dog {
public:
~Dog() { cout << "dog destroyedn"; }
};
class Yellowdog : public Dog {
public:
~Yellowdog() { cout << "Yellow dog destroyed.n"; }
};
class DogFactory {
public:
static shared_ptr<Dog> createYellowdog() {
return shared_ptr<Yellowdog>(new Yellowdog());
}
};
int main(int argc, char *argv[]) {
auto ptr = DogFactory::createYellowdog();
cout << ptr.use_count() << endl;
return 0;
}
在这种情况下,输出是yellowdog destroyed
,然后是dog destroyed
。但为什么呢?为什么使用shared_ptr
可以省略~Dog
之前的虚拟关键字?
之所以会发生这种情况,是因为shared_ptr
在创建第一个shared_ptr
时创建的控制块中存储类型已擦除的deleter。在您的例子中,shared_ptr
是为黄狗创建的,deleter将调用黄狗析构函数。
当您将一个shared_ptr
复制(或复制构造)到另一个时,它们共享相同的控制块,并且新的共享ptr将从原始ptr调用deleter,也就是将调用yellowdog析构函数的ptr。
然而,它并没有真正使类具有多态性,也不适合工厂实现——类中的任何其他非虚拟函数都将基于静态类型shared_ptr
进行调用,您不希望在多态类中出现这种情况。
SergeyA所说的完全正确,对于那些想要查看真实代码的人来说,以下是LLVM的shared_ptr实现(我只显示了与解释相关的部分代码):
template<class _Tp>
class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
{
//Constructor taking a pointer
template<class _Yp, class = _EnableIf<
_And<__compatible_with<_Yp, _Tp>>::value>
>
explicit shared_ptr(_Yp* __p) : __ptr_(__p) {
unique_ptr<_Yp> __hold(__p);
typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT > _CntrlBlk;
__cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
__hold.release();
__enable_weak_this(__p, __p);
}
...
//other code omited
}
//Move Constructor
template<class _Tp>
template<class _Yp>
inline
shared_ptr<_Tp>::shared_ptr(shared_ptr<_Yp>&& __r,
typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type)
_NOEXCEPT
: __ptr_(__r.__ptr_),
__cntrl_(__r.__cntrl_)
{
__r.__ptr_ = nullptr;
__r.__cntrl_ = nullptr;
}
步骤1:当您调用shared_ptr<Yellowdog>(new Yellowdog());
时,首先将调用获取指针的构造函数,在这种情况下,_Tp与_Yp相同。这里还创建了一个控制块,以_Yp*(所以Yellowdog*)作为输入,并将其保存在内部。然后,创建的控制块被保存为shared_ptr 内的虚拟接口(类型已擦除)
第2步:然后在函数返回期间,CCD_ 12被转换为CCD_。在这种情况下,调用shared_ptr<Dog>
的move构造函数,但是,这次_Tp是Dog,_Yp是Yellowdog。因此CCD_ 15正在发生从Yellowdog*到Dog*的隐式转换。但对于__ctrl_
,它只是一个简单的副本,没有任何变化。
步骤3:在销毁过程中,__ctrl_
中的deallocater将被调用,由于它从一开始就保存了Yellowdog*,因此它可以直接调用其析构函数,进而调用其父(Dog)析构函数。因此,在这种情况下,Dog不需要虚拟析构函数。
- 多态性和功能结合
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- C++boost序列化多态性问题
- 如何查找哪个类对象位于数组的特定索引上(多态性)
- 如何在多线程中正确使用unique_ptr进行多态性?
- 具有智能指针的多态性
- 在 C++ 中在堆栈上创建实例时如何保持多态性?
- 继承/多态性 - 我是否被迫使用"protected"变量?
- C++ 多态性在代码::块 17.12 中不起作用
- C++ 泛型和多态性:这种模式可行吗?
- 为什么我们实际上需要运行时多态性?
- 如何在这个简单的最小示例中实现多态性?
- 如何使用静态多态性在 int 和指针类型之间进行转换?
- 无法初始化已知大小的矢量指针,该大小不会因多态性而更改
- 运行时多态性和dynamic_cast需要澄清
- 多态性和共享_ptr通过引用传递
- 是否可以将多态性类存储在共享内存中
- c++在没有虚拟析构函数的多态性中共享ptr
- 具有共享遗传的C++多态性