std::shared_ptr
有一个漂亮的模板化构造函数,可以自动为其给定的类型创建正确的删除器(该链接中的构造函数#2)。
直到现在,我(错误地)认为std::unique_ptr
有一个类似的构造函数,但是当我运行以下代码时:
#include <memory>
#include <iostream>
// Notice nothing is virtual
struct Foo
{
~Foo() { std::cout << "Foo\n"; }
};
struct Bar : public Foo
{
~Bar() { std::cout << "Bar\n"; }
};
int main()
{
{
std::cout << "shared_ptr:\n";
std::shared_ptr<Foo> p(new Bar()); // prints Bar Foo
}
{
std::cout << "unique_ptr:\n";
std::unique_ptr<Foo> p(new Bar()); // prints Foo
}
}
I was surprised to learn that unique_ptr
doesn't call Bar
's destructor.
创建一个具有正确删除指针的unique_ptr
的干净,简单和正确的方法是什么?特别是如果我想存储这些的完整列表(即std::vector<std::unique_ptr<Foo>>
),这意味着它们都必须具有异构类型?
(原谅这个糟糕的头衔;随意提出更好的头衔)
答案 0 :(得分:7)
你应该使用Foo
virtual
的析构函数。无论您是否使用unique_ptr
,这都是很好的做法。这也将照顾你正在处理的问题。
答案 1 :(得分:5)
这是一种方式:
{
std::cout << "unique_ptr<Bar, void(void*)>:\n";
std::unique_ptr<Foo, void(*)(void*)> p(
new Bar(), [](void*p) -> void { delete static_cast<Bar*>( p ); }
); // prints Bar Foo
}
这种方法的一个主要问题是unique_ptr
支持转换为逻辑“指向基类的指针”,但标准并不保证转换为void*
将产生相同的地址。实际上,如果基类是非多态的,而派生类是多态的,引入一个vtable ptr,从而可能会稍微改变内存布局,这只是一个问题。但是在那个可能但不太可能的情况下,删除器中的强制转换会产生一个不正确的指针值,然后爆炸。
因此,对于此类转换,上述内容并非正式安全。
与shared_ptr
大致相同(shared_ptr
支持转换为逻辑指针到基础),您还需要存储原始void*
指针,以及删除者。
通常,当您控制最顶层的基类时,请将其析构函数设为虚拟。
照顾好一切。