我有一个C ++场景,在我没有预料到的情况下调用孩子的析构函数。最小的复制品如下:
#include <cstdio>
#include <memory>
using namespace std;
class Parent {
public:
};
class Child : public Parent {
public:
~Child() {
printf("Got here\n");
}
};
int
main()
{
shared_ptr<Parent> x(new Child);
}
通常这样的事情就是一个错误。开发人员打算调用子析构函数,正确的操作是将空虚拟析构函数插入父级。然而,令我震惊的是,G ++ 4.4.7(是的,我知道它已经老了)和clang 3.4.2编译它,这样就可以调用子析构函数
。这是否符合标准?
答案 0 :(得分:5)
即使shared_ptr
没有特殊魔法,delete
父指针与非虚拟析构函数只是未定义的行为,所以结果(调用子析构函数)肯定会顺从。
但在这种情况下shared_ptr
&#34;记得&#34;传递给它的原始对象的类型,并通过子指针(通过其存储的删除器)销毁它。
答案 1 :(得分:0)
一个独特的ptr会搞砸到这里。但共享ptr会做一些&#34;魔术&#34;这里。
shared_ptr<T> has two things they manage; the
T *`和引用计数块。
引用计数块包含两个atomic<std::size_t>
,一个用于强引用,一个用于弱引用,以及一个类型擦除的删除器。此std::function
- 类似已删除的删除器会记住如何删除您拥有的内容。
使用shared_ptr
构建任何U*u
时,默认情况下会将[u]{std::default_delete<U>{}(u);}
存储在该删除工具中。
实际上,它会记住如何根据传入的类型删除对象。
shared_ptr
非常灵活。
您可以传入自定义删除器来替换默认删除器,您可以使用别名构造函数来分割使用的T*
存储的引用计数块,您可以使用make_shared
来分配引用在同一内存分配中计数块和T
。
引用计数块的开销是它存储删除器的原因;考虑到我们无论如何都需要块,它并不被认为过于昂贵。相比之下,unique_ptr
默认情况下没有这样的事情;您必须明确添加删除器,并且如果您需要,您必须默认管理shared_ptr为您执行的所有花哨技巧。对于原始拥有指针,unique_ptr
基本上没有开销; shared_ptr
具有明显的开销,但与内存分配开销相比通常很小。