C++ 创建独特的ptr<;基地>;当基类具有受保护的析构函数时 类基{ 公众: Base(){} 虚空打印()常量=0; 受保护的: virtual~Base(){std::cout
C++ 创建独特的ptr<;基地>;当基类具有受保护的析构函数时 类基{ 公众: Base(){} 虚空打印()常量=0; 受保护的: virtual~Base(){std::cout,c++,shared-ptr,destructor,unique-ptr,C++,Shared Ptr,Destructor,Unique Ptr,std::unique\u ptr无法访问Base的析构函数,因为它受保护std::shared\u ptr使用多态删除器,因此创建新的std::shared\u ptr时只需要访问Base的析构函数 class Base { public: Base() {} virtual void print()const = 0; protected: virtual ~Base() { std::cout << "Base destructor\n\n"; } };
std::unique\u ptr
无法访问Base
的析构函数,因为它受保护
std::shared\u ptr
使用多态删除器,因此创建新的std::shared\u ptr
时只需要访问Base
的析构函数
class Base {
public:
Base() {}
virtual void print()const = 0;
protected:
virtual ~Base() { std::cout << "Base destructor\n\n"; }
};
int main()
{
//std::vector<std::unique_ptr<Base>> v1;
//The line above won't compile because: 'Base::~Base': cannot access protected member declared in class 'Base'
std::vector<std::shared_ptr<Base>> v2;
return 0;
}
在您的情况下,
Base
恰好是抽象的,所以您可以使用std::shared\u ptr
,因为您永远不需要编写std::make\u shared()
。只要Base
的子类具有public
析构函数,std::make_shared
将能够访问它们,而不会给您带来错误。局部变量v1
和v2
具有自动存储持续时间,当它们超出范围时将自动销毁。std::vector
在这里是不相关的:在vector::~vector()
内部,编译器将为元素析构函数生成代码。即使向量始终为空(这是一个运行时属性!),也必须生成此代码。因此,让我们简化代码:
struct shared_ptr {
~shared_ptr() {
deleter();
}
void (*deleter)(); // pointer to function that destroys the object
};
// shared_ptr doesn't try to call the destructor directly so we don't need access
// so this is ok
shared_ptr a;
shared_ptr make_shared() {
// here we generate (with templates) a function that calls Base::~Base
// then we set "deleter" to point to that function
// the destructor has to be accessible for us to do this
}
// so we get an error here
shared_ptr b = make_shared();
struct unique_ptr {
~unique_ptr() {
// unique_ptr calls the Base destructor directly
// unique_ptr needs access to the destructor to instantiate the type
}
};
// so we get an error here
unique_ptr c;
要为delete ptr
生成代码,编译器需要可访问的析构函数。它受保护,因此编译失败
现在让我们看一下v2
。编译器也必须生成析构函数。但是shared_ptr
有一个托管对象的类型擦除删除器。这意味着托管对象析构函数将通过虚拟函数间接调用:
~unique_ptr() {
delete ptr;
}
要为deleter->destroy()
生成代码,您根本不需要访问Base::~Base()
。shared\u ptr
的默认构造函数只需将deleter
设置为空指针:
struct shared_ptr_deleter_base {
virtual void destroy() = 0;
virtual ~shared_ptr_deleter_base() = default;
};
~shared_ptr() {
// member shared_ptr::deleter has type shared_ptr_deleter_base*
if (deleter)
deleter->destroy();
}
这就是为什么std::shared_ptr v2;
编译:不仅在运行时没有调用Base::~Base()
,编译器在编译时也不会生成任何调用
我们考虑一下这条线:
shared_ptr() {
deleter = nullptr;
}
这里的shared\u ptr\u deleter
是从shared\u ptr\u deleter\u base
派生的一个具体类:
template<class U>
shared_ptr(U* ptr) {
deleter = new shared_ptr_deleter<U>(ptr);
}
模板
结构共享\u ptr\u删除器:共享\u ptr\u删除器\u基{
T*ptr;
共享的删除器(T*p):ptr(p){}
虚空销毁(){
删除ptr;
}
};
要为采用new Base()
的构造函数生成代码,编译器必须为shared\u ptr\u deleter::destroy()
生成代码。现在它失败了,因为Base::~Base()
不可访问
(*)我只提供了简化的定义,只是为了演示基本思想,而没有涉及与理解问题无关的所有细节。这是否回答了您的问题?嘿,评论何时从可能重复更改为回答了您的问题:P@appleapple谢谢你,你提到的问题没有o很有帮助。Kerndog73和Evg的答案更直截了当地解决了我的问题。@appleapple@LightnessRacesBY-SA3.0谢谢,好吧,我真的不喜欢新的措辞:/In
shared\u ptr
构造函数(或make\u shared()
)编译器不仅仅是“集”deleter
函数指针,但也为它指向的函数(deleter)生成代码。对于std::shared\u ptr b;
没有生成此类代码,因此编译成功。
shared_ptr() {
deleter = nullptr;
}
std::shared_ptr<Base> v2(new Base());
template<class U>
shared_ptr(U* ptr) {
deleter = new shared_ptr_deleter<U>(ptr);
}
template<class T>
struct shared_ptr_deleter : shared_ptr_deleter_base {
T* ptr;
shared_ptr_deleter(T* p) : ptr(p) {}
virtual void destroy() {
delete ptr;
}
};