2012-01-05 95 views
0

假設我有類似以下的類:遞歸析構函數在C++

#include <vector> 

class element 
{ 
public: 
    element(); 
    ~element(); 

    virtual void my_func(); 

private: 
    std::vector<element*> _elements; 
}; 

我怎麼會去執行析構函數?

我在想這樣的事情,但我不確定。我很擔心內存泄漏,因爲我對C++比較陌生。

void element::destroy() 
{ 
    for(int i = 0; i < _elements.size(); ++i) 
     _elements.at(i)->destroy(); 

    if(this != NULL) 
     delete this; 
} 

element::~element() 
{ 
    destroy(); 
} 

謝謝。

PS: 這裏是一個示例主:

int main() 
{ 
    element* el_1 = new element(); 
    element* el_2 = new element(); 
    element* el_3 = new element(); 

    el_1.add_element(el_2); 
    el_2.add_element(el_3); 

    return 0; 
} 

而且,如果我這樣做(又名不要使用new):

int main() 
{ 
    element el_1; 
    element el_2; 
    element el_3; 

    el_1.add_element(&el_2); 
    el_2.add_element(&el_3); 

    return 0; 
} 
+1

你可以調用'delete _elements.at(i)',你不應該在析構函數中測試'this'不是null。它不能爲空。 – 2012-01-05 21:04:08

+0

會調用'delete _elements.at(i)'正確處理所有的子元素嗎?換句話說,他們是否會按照正確的順序處理(從下到上)?因爲我認爲如果我刪除一個'_elements'中仍然有東西的元素,那將是一個泄漏。 – Eric 2012-01-05 21:06:47

+1

當你「刪除某些東西」時,調用'something'類的析構函數,然後指針被釋放。 – 2012-01-05 21:08:20

回答

8
element::~element() 
{ 
    typedef std::vector<element*>::const_iterator iterator; 
    for (iterator it(_elements.begin()); it != _elements.end(); ++it) 
     delete *it; 
} 

delete this在析構函數永遠是錯的:this已經被破壞了!另外,你需要聲明一個拷貝構造函數和拷貝賦值操作符(或者讓它們不確定,使你的類不可複製,或者提供一個合適的定義來拷貝樹)。

或者(最好),你應該使用智能指針的容器爲_elements。例如,

std::vector<std::unique_ptr<element>> _elements; 

element被破壞,其_elements容器將自動銷燬。當容器被銷燬時,它會破壞它的每個元素。 A std::unique_ptr擁有它指向的對象,並且當std::unique_ptr被銷燬時,它將銷燬它指向的元素。

通過在這裏使用std::vector<std::unique_ptr<element>>,您不需要提供自己的析構函數,因爲所有這些內置函數都會爲您進行清理。

如果您希望能夠複製element樹,您仍然需要提供自己的複製構造函數和複製賦值操作符來克隆該樹。但是,如果您不需要樹可以複製,則不需要像您自己管理內存那樣聲明覆制操作:容器本身不可複製,因此它作爲成員變量的存在將禁止隱式生成的複製操作。

+0

那麼,在這種情況下,你能否想到'delete * it'是對'〜element'的遞歸調用? – Eric 2012-01-05 21:10:50

+1

@Eric:那當然正是發生了什麼事情。 – 2012-01-05 21:11:49

+1

@Eric:實際上,刪除對象首先會調用對象的析構函數(它應該清理它所負責的任何對象),然後釋放對象佔用的內存。 – sbi 2012-01-05 21:18:28

3
class element 
{ 
public: 
    element(); 
    ~element(); 

private: 
    std::vector<element*> _elements; 
}; 

根據Rule of Three,這沒有定義拷貝構造函數賦值運算符


element::~element() 
{ 
    destroy(); 
} 

從析構函數調用delete this(這是什麼destroy()一樣)是總是錯,因爲析構函數是在當前對象被刪除所謂。


for(int i = 0; i < _elements.size(); ++i) 
    _elements.at(i)->destroy(); 

雖然這確實呼籲你打電話destroy()的對象delete的工作,這種安排的缺點是,你不得破壞不是通過調用destroy()等這樣的對象。特別是,該

element el_1; 

將是一個問題,因爲e1_1會當它落在範圍外的海灣調用析構函數的自動銷燬。由於這會調用destroy(),該對象在未使用new分配的對象上調用delete this,這會導致未定義的行爲。 (如果你幸運的話,馬上就會打到你的臉上。)

這將是更好的選擇刪除析構函數的delete this,並且只有析構函數刪除對象的矢量對象:

for(std::vector<element*>::size_type i = 0; i < _elements.size(); ++i) 
    delete _elements[i]; 

if(this != NULL) 
    delete this; 

檢查一NULL尼斯的指針是從來沒有必要,b因爲delete被定義爲在傳遞給它的指針爲NULL時不做任何事情。

+0

最後一段有誤導性; 「if」從來就不是必需的,不是整個片段(儘管作爲一個相關說明,當然'刪除這個'是很少用的!) – 2012-01-05 21:10:56

+1

@Lightness:我正在改進這個過程。 ':'' – sbi 2012-01-05 21:16:57

2
  1. delete this不應該存在,當對象是已經在破壞由定義。

  2. 如果您複製element,則其成員vector中的所有指針也會被複制;那麼當原稿超出範圍時,它們的指尖就會被銷燬,並且element副本有一個vector懸掛指針。您需要一個副本和分配操作員。

所以,這個基本的開端已經造成了兩個非常嚴重的錯誤。這是設計要避免的標誌。似乎並不清楚使用它的程序員的所有權語義是什麼。

爲什麼這裏需要動態分配?爲什麼不自己存儲element的對象?

#include <vector> 

class element 
{ 
public: 
    void add_element(element const& e) { 
     _elements.push_back(e); 
    } 
private: 
    std::vector<element> _elements; 
}; 

這會微妙地改變你的程序,如果你以前有相同的element進入不同的其他elements的多個引用,但你必須自己決定的依賴關係是錯綜複雜的因素是否是你所需要的東西。

+0

感謝您的評論 - 我需要使用指針的原因是因爲'element'旨在成爲具有'virtual'功能的基類。這是我看到確保虛函數的正確形式執行的唯一方法。 – Eric 2012-01-05 21:14:39

+1

@Eric:好的;那可能是有道理的。不過,考慮一個像std :: shared_ptr這樣的包裝指針實現。 – 2012-01-05 21:18:16

0

不,它不會那樣工作。你永遠不會刪除this。你不知道。基本上,拿出你的銷燬方法,把所有東西都放進~element(),除了delete this部分。而不是調用銷燬方法,只需delete _elements[i];

+0

'刪除此;'有效且偶爾有用。 – 2012-01-05 21:15:26

+0

是的,但你仍然從未這樣做過,就像你從未做過某些其他事情一樣,因爲它很方便,你有時最終會做這些事情。 – Cubic 2012-01-05 22:25:22