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;
    }
};