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向量进行编译?
局部变量v1
和v2
具有自动存储时间,当他们去的范围之会自动销毁。在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
,该参数可能不同于T
in 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] 删除。
我来说两句