2017-03-28 67 views
0

我有一個C++的場景,在我沒有想到的情況下調用了子的析構函數。一個最小的repro如下:使用默認構造函數的父類;子類'析構函數意外地被稱爲

#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(是的,我知道這是舊的)和叮噹聲3.4.2編譯這樣的子進程調用

這是否符合標準?

+0

@chris是啊我檢查了一下,它確實是這樣,不確定它是否有用。 –

+0

@ n.m。,不能說我個人對此有需要,但當你這樣做的時候,這是一件好事。 – chris

+0

@ n.m。這很有用,因爲您可以使用'shared_ptr '來管理派生對象,而不需要虛擬析構函數的開銷 –

回答

5

那麼即使shared_ptr沒有特別的魔法,delete ing父指針與非虛析構函數只是未定義的行爲,所以結果(調用子析構函數)肯定會符合。

但在這種情況下,shared_ptr「記住」您傳入的原始對象的類型,並通過子指針(通過其存儲的刪除器)銷燬它。

+0

恩,存儲的刪除器不使用模板類型,也不使用用於創建它的類型? – NathanOliver

+0

不,大致它存儲'std :: function'(或等價物)到'deleter ',其中'U'是構造函數的參數類型,而不是'shared_ptr'模板類型。它甚至適用於'shared_ptr '等。 –

0

一個獨特的ptr會搞砸在這裏。但共享ptr在這裏做了一些「魔術」。

shared_ptr<T> has two things they manage; the T *`和引用計數塊。

引用計數塊包含兩個atomic<std::size_t> s,一個用於強引用,另一個用於弱引用,以及一個類型擦除刪除器。這std::function類型刪除刪除記得如何刪除你自己的東西。

當您使用U*u構造任何shared_ptr時,它默認將[u]{std::default_delete<U>{}(u);}存儲在該刪除器中。

實際上,它會記住如何根據傳入的類型要刪除的對象。

shared_ptr是非常靈活的。

您可以在定製刪除傳遞給替換默認的一個,你可以使用別名構造分裂從存儲在T*使用引用計數塊,你可以使用make_shared分配的引用計數塊和一個T相同的內存分配。

引用計數塊的開銷是爲什麼它存儲刪除器;無論如何,因爲我們需要該區塊,所以它並不被視爲過於昂貴。相比之下,unique_ptr默認沒有這樣的事情;你必須明確地添加一個刪除器,並且你必須管理shared_ptr爲你默認的所有技巧,如果你想要的話。 unique_ptr在原始擁有指針上基本上具有零開銷; shared_ptr具有明顯的開銷,但通常與內存分配開銷相比較小。

相關問題