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 进行编译?
最佳答案
局部变量v1
和v2
具有自动存储期限,超出范围时自动销毁。 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/