当基类具有受保护的析构函数时,创建unique_ptr <Base>

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

创建向量时试图调用析构函数的是什么?为什么不为unique_ptr向量进行编译,而是为shared_ptr向量进行编译?

v

局部变量v1v2具有自动存储时间,当他们去的范围之会自动销毁。std::vector这里是无关紧要的:内部vector::~vector()编译器将生成元素的析构函数的代码。即使向量始终为空(这是运行时属性!),仍然必须生成此代码。因此,让我们简化代码:

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

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

~unique_ptr() {
    delete ptr;
}

要为生成代码delete ptr,编译器需要可访问的析构函数。它受到保护,因此编译失败。

现在让我们看一下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,该参数可能不同于Tin 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()无法访问。

(*)我只给出简化的定义,只是为了说明基本思想,而没有涉及与理解所讨论问题无关的所有细节。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在具有受保护的析构函数的类中使用unique_ptr

使用unique_ptr保护的析构函数

通过实例化具有保护析构函数的父类型的基类创建的可用内存

具有unique_ptr成员和自定义析构函数的对象的向量

unique_ptr析构函数的优势

为什么在使用unique_ptr时没有调用析构函数?

具有受保护的析构函数的类数组的动态分配

为了在测试中调用析构函数(对于测试),Static-Casting Base 类到 Derived 类有多可怕?

如何允许 std::unique_ptr 访问类的私有析构函数或使用私有析构函数实现 C++ 工厂类?

为什么要为具有非平凡析构函数的类声明constrexpr构造函数(例如unique_ptr,std :: variant)

如何在具有受保护的析构函数和公共销毁方法的3d派对类上使用shared_ptr

具有显式析构函数和std :: unique_ptr <>成员的类不能在std :: vector <>中使用吗?

将unique_ptr <Derived>&传递给接受unique_ptr <Base>&的函数

在声明为返回unique_ptr <Base>的函数中返回unique_ptr <Derived>

具有虚拟析构函数的基类的子类中的默认析构函数

std :: unique_ptr析构函数构造函数顺序

unique_ptr :: release()是否调用析构函数?

unique_ptr 两次调用析构函数

当包含它的对象调用其析构函数时,unique_ptr是否会取消分配?

如果A有析构函数,std :: unique_ptr <A>什么时候需要特殊的删除器?

受保护的析构函数禁止在堆栈上创建派生类的对象?

当析构函数在类主体中声明为unique_ptr作为同一类的成员时发生编译器错误

当基类的成员的析构函数具有非空的no指定符和主体时,在析构函数上使用C2694

“类型为'A'的临时类型具有受保护的析构函数”,但类型为B

删除另一个类中带有受保护析构函数的对象

C ++ unique_ptr <Base>指向派生的ptr

如何在Base析构函数中将Base *与Derived *列表进行匹配?

使析构函数受保护以通过带有虚函数的基类禁用删除调用是一个好主意吗

与“受保护的虚拟析构函数”相比,拥有“受保护的非虚拟析构函数”有什么好处?