2014-10-05 93 views
5

std::shared_ptr有一个漂亮的模板化构造函数,它自动为其给定类型创建正确的删除器(该链接中的构造器#2)。有没有一种方便的方法让unique_ptr自动创建一个像shared_ptr这样的删除器?

直到刚才,我(错误地)认为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

'unique_ptr'的deleter属于它的类型。如果你创建了一个'std :: unique_ptr '的向量,那么它们都将使用'std :: default_delete '。 – 2014-10-05 05:01:34

+0

http://stackoverflow.com/questions/6829576/why-does-unique-ptr-have-the-deleter-as-a-type-parameter-while-shared-ptr-doesn – 2014-10-05 05:05:51

+3

我认为问题出在你的身上类,而不是'std :: unique_ptr'。你的析构函数需要是虚拟的。 – Galik 2014-10-05 05:55:38

回答

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*指针,与缺失者一起。


通常,当您控制最顶级的基类时,使其析构函数变为虚拟。

这需要照顾一切。

+0

对于有问题的情况,double double static_cast'如何将'void *'强制转换为'Foo *'然后转换为'Bar *'? – 2014-10-05 09:00:17

+0

@ T.C。问题是,deleter不知道传递给'void *'的指针类型。根据类型的不同,它可以指向不同的地址。但是现在你让我思考这个问题,问题在于'void *'丢弃了太多的信息。我认为''Foo *'参数类型都可以,然后在删除器中转换为'Bar *'。或者,这有什么问题吗?今天对我来说有点晚了。 – 2014-10-05 09:08:26

+0

传递给删除者的指针并不总是有用,即使类型正确。有时static_cast可能是不可能的,例如虚拟继承(http://stackoverflow.com/questions/7484913/why-cant-static-cast-be-used-to-down-cast-when-virtual-inheritance-is-参与)。我认为唯一真正强大的解决方案是让删除者记住原始指针值(从'new'返回)。这意味着删除者正式地忽略了它的论点,因此它可能是'void *'。最后,一旦你支付了这个内存的'价格',你也可以添加一个'deep_copy'而不需要额外的成本。 – 2015-07-29 09:14:27

7

您应该制作Foovirtual的析构函数。无论您是否使用unique_ptr,这都是很好的做法。这也将照顾你正在处理的问题。

+0

我同意这是应该做的,但在这种情况下,我不能改变类 – Cornstalks 2014-10-05 06:41:25

相关问题