2014-10-05 92 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

相關問題