c++ - 当基类具有 protected 析构函数时创建 unique_ptr<Base>

标签 c++ shared-ptr destructor unique-ptr

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

当我创建 vector 时,什么试图调用析构函数?为什么它不会针对 unique_ptr vector 进行编译,但会针对shared_ptr vector 进行编译?

最佳答案

局部变量v1v2具有自动存储期限,超出范围时自动销毁。 std::vector在这里无关紧要:在 vector::~vector() 内编译器将为元素析构函数生成代码。即使 vector 始终为空(这是运行时属性!),仍然需要生成此代码。那么让我们简化一下代码:

std::unique_ptr<Base> v1;
std::shared_ptr<Base> v2;

何时 v1超出范围,必须将其销毁。编译器生成的析构函数归结为(*):

~unique_ptr() {
    delete ptr;
}

delete ptr 生成代码,编译器需要可访问的析构函数。由于它是 protected ,所以编译失败。

现在让我们看看v2 。编译器也必须生成析构函数。但是shared_ptr有一个用于托管对象的类型删除删除器。这意味着将通过虚拟函数间接调用托管对象析构函数:

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

deleter->destroy() 生成代码,您不需要访问Base::~Base()根本不。 shared_ptr的默认构造函数只需设置deleter指向空指针:

shared_ptr() {
    deleter = nullptr;
}

这就是为什么 std::shared_ptr<Base> v2;编译:不仅Base::~Base()不在运行时调用,编译器在编译时不会生成任何调用。

让我们考虑这一行:

std::shared_ptr<Base> v2(new Base());

现在调用以下构造函数(请注意,它是一个带有单独参数 U 的模板,可以与 T 中的 shared_ptr<T> 不同):

template<class U>
shared_ptr(U* ptr) {
    deleter = new shared_ptr_deleter<U>(ptr);
}

这里shared_ptr_deleter是派生自 shared_ptr_deleter_base 的具体类:

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

为采用 new Base() 的构造函数生成代码,编译器必须生成 shared_ptr_deleter<Base>::destroy() 的代码。现在它失败了,因为 Base::~Base()无法访问。

(*) 我仅提供简化的定义,只是为了演示基本思想,而不涉及与理解所讨论问题无关的所有细节。

关于c++ - 当基类具有 protected 析构函数时创建 unique_ptr<Base>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59694451/

相关文章:

c++ - 从这个对象的方法开始的线程中销毁类对象的最佳实践

c++ - 使用 Eclipse UML 插件 Papyrus 生成 C++ 构造函数和析构函数

c++ - 为什么我的程序在 _fini 中花费了 85% 的 CPU 周期?

函数参数中的c++函数定义

c++ - Maven nar 构建中的 GoogleTest 编译错误 - 未定义对`testing::internal::MakeAndRegisterTestInfo 的引用

C++ std::shared_ptr 通过 operator= 赋值

C++ Xerces-c : Initializing a shared_ptr

c++ - 编译器生成的抽象基类的析构函数是虚拟的吗?

c++ - 如何编译仅在头文件中的 C++ 接口(interface)

c++ - 根据运行时参数避免模板实例化的代码重复