2012-09-26 156 views
0

在我的應用我創建並返回充滿了動態分配的對象從派生類像這樣的數組:刪除動態創建的對象

void someGetter(std:vector<DerivedClass> & returnV) 
{ 
    BaseClass* base = object->clone(); // "object" is a "unique_ptr<BaseClass>" 

    DerivedClass* derived = dynamic_cast<DerivedClass*> (base); 

    if (derived != nullptr) 
    { 
     returnV.push_back(*derived); 
    } 
    else 
    { 
     delete base; 
    } 
} 

這顯然造成內存泄漏(valgrinds幫助我在這裏),因爲派生永遠不會被刪除。

我試圖釋放所分配的內存是這樣的:

delete &returnV[0]; 

它不給任何編譯錯誤/警告和代碼仍然運行正常。但valgrind會在該行代碼中報告一些額外的錯誤(無效讀取,無效的自由),並且泄漏仍然存在。

有沒有什麼方法可以釋放像這樣返回的內存?或者我應該返回unique_ptr而不是對象?

+0

東西(以及作爲地下室裏的人)尖叫「可怕的虐待」......如果你有'EvenMoreDerivedClass'呢?這將在副本中瓜分... –

+0

對於C++ 11的問題:你真的應該使用'的std :: unique_ptr'(或'的std :: shared_ptr',根據您的使用情況)。手動內存管理實際上不被推薦,因爲內存泄漏的潛力(由於缺少像代碼中的delete一樣,或者由於不處理引發異常的情況)非常高。所以我的建議永遠不會(通常有一些例外,儘管不是很多)使用手動管理,並始終使用智能指針(或諸如boost指針容器之類的東西)。 – Grizzly

+0

@Grizzly與Kerrek的第二個答案我不這樣做手工的內存管理了 – Minion91

回答

2

首先,設計似乎很可疑的對我說:你有一次一個多態的層次結構,並持有值該層次結構中的特定成員的的容器。你邀請的問題沒有盡頭。有一個std::vector<std::unique_ptr<Base>>似乎更合理。

總之,這裏的適度安全,高效插入容器中唯一的那些對象的動態類型精確匹配方式。它假定層次結構中的每個類都有可訪問的拷貝構造函數。

void someGetter(std:vector<DerivedClass> & returnV) 
{ 
    if (typeid(*object) != typeid(DerivedClass)) { return; } 

    returnV.insert(static_cast<DerivedClass&>(*object)); 
} 

的這個語義是從你的略有不同,因爲您的代碼將允許其中*object是嚴格的多個派生類型比DerivedClass的情況下,和複製到載體將切片的對象。目前的代碼不會遇到這個問題。


更新(您的評論後):如果DerivedClass確實final(並請註明它是這樣!),那麼下面確實沒有typeid:我內心

void someGetter(std:vector<DerivedClass> & returnV) 
{ 
    if (DerivedClass * p = dynamic_cast<DerivedClass *>(object.get())) 
    { 
     assert(typeid(*p) == typeid(DerivedClass)); // beware of slicing! 

     returnV.insert(*p); 
    } 
} 
+0

嗯,起初我使用的是unique_ptr,但是我的項目協調員讓我把它變成值。 Wat例外是你想趕上?爲什麼一個static_cast? – Minion91

+0

你可以假設DerivedClass永遠不會被子類化。 – Minion91

+0

@ Minion91:對不起,如果你讓我使用複製構造函數,異常問題就會消失。如果你堅持使用'clone'(如果你還想要價值語義,這很愚蠢),那麼你必須處理異常。 –

6

如果你要創建一個保存Derived那麼代碼是除了內存泄漏正確的向量。請注意,您需要釋放的對象不是一個容器中(這是一個副本),而是你克隆了一個:

void someGetter(std:vector<DerivedClass>& returnV) 
{ 
    BaseClass* base = object->clone(); (object is a unique_ptr<BaseClass>) 
    DerivedClass* derived = dynamic_cast<DerivedClass> (base); 
    if (derived != nullptr) 
    { 
     returnV.push_back(*derived); 
    } 
    delete base; 
} 

此外,如果clone()做什麼它說(即它克隆的對象)那麼您可以先用dynamic_cast進行測試,以避免該操作base對象是否爲 a DerivedClass。如果它插入到容器中,則避免克隆。

+0

克隆不正是它說,這樣的push_back自動外幣對象的副本?與拷貝構造函數? – Minion91

+0

它甚至會更快做' typeid的(* objec t)== typeid(DerivedClass)',非?也更安全,因爲它不會切分和切割「EvenMoreDerivedClass」。 –

+0

@ Minion91:是的,容器容納*值*,它將使用複製構造函數存儲對象的一個​​副本(在您的情況下,在其他一些情況下,它可能*在C++ 11中移動*或構建到位。 。但無論如何容器將持有自己的副本傳入的對象) –

3

簡單的答案 - 總是刪除基地。

if (derived != nullptr) 
{ 
    returnV.push_back(*derived); 
} 
delete base; 

向量需要派生的副本 - 因此不再需要克隆對象。

[更新]

我希望你有虛析構函數中BaseClass - 如果不是 - 然後將其添加。

還有一個警告:可能會發生:base->clone()回報的東西比得到的多源性:

那麼這個代碼成功,即使真正的階級基礎是MoreDerivedClass

DerivedClass* derived = dynamic_cast<DerivedClass> (base); 

您可以使用typeid()來檢查實際類型的基地...


[UPDATE2]
考慮改變一點你的設計 - 並保持你的基地的克隆在DerivedClass中的unique_ptr的載體:

void someGetter(std:vector<std::unique_ptr<DerivedClass>> & returnV) 
{ 
    if (dynamic_cast<DerivedClass*>(base.get()) != nullptr) 
    { 
     returnV.push_back(dynamic_cast<DerivedClass*>(base->clone())); 
    } 
} 
1

是,push_back使用拷貝構造函數。我認爲大衛是說,你的代碼應該是

void someGetter(std:vector<DerivedClass>& returnV) 
{ 
    DerivedClass*derived = dynamic_cast<DerivedClass*>(object.get()); 
    if (derived != nullptr) 
     returnV.push_back(*derived); 
} 

避免克隆和刪除完全。

注加入編輯:我們不得通過從unique_ptr<>.get()獲得的指針上可能潛在地保留一份,不畏unique_ptr整點的任何功能。上面的代碼沒有這樣做。

+0

是不是用得到那種邪惡?它的魅力擊敗了unique_ptr的目的不是? – Minion91

+0

@ Minion91 **否** 1)如果它本身就是邪惡的,那麼它就不在標準庫中。 2)在這裏我們沒有泄漏指針(你可以用'.get()'來完成),而只是將它用作指針,即我們將它傳遞給一個需要實際指針類型的函數。 – Walter

+0

那麼你在哪裏幹惡劣之間的界限呢?因爲你當然可以用unique_ptr.get() – Minion91