是否有一种方便的方法可以让unique_ptr自动拥有像shared_ptr这样的删除器?

时间:2014-10-05 04:56:00

标签: c++

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>>),这意味着它们都必须具有异构类型?

(原谅这个糟糕的头衔;随意提出更好的头衔)

2 个答案:

答案 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*指针,以及删除者。


通常,当您控制最顶层的基类时,请将其析构函数设为虚拟。

照顾好一切。