2013-02-16 45 views
1

我在想,如果默認的類析構函數在被調用時實際上做了什麼。做默認析構函數做什麼?

我一直在研究它,我發現如果我用一個調用它自己的析構函數的函數創建一個類,它根本不會做任何事情(即所有變量都保持不變,並且實例仍然存在並且可用)。

這是否意味着類的析構函數可以被認爲是所有類都具有的繼承的虛函數,並且可以重新定義它(刪除指針等並清除成員變量),但是如果不重新定義它什麼都不做?

如果是這樣,不能將析構函數實質上用作「清除所有數據」類型的函數,並通過清除動態內存分配變量並重新使用它而不是獲取計算機來使代碼的某些部分更有效在堆上找到新的內存塊?

謝謝。

+0

如果調用析構函數手動釋放內存,如果該對象被破壞,然後會發生什麼?我不確定你的目標是什麼,但它可能更適合簡單的成員函數。 – Bingo 2013-02-16 12:24:51

+0

回答這個問題可能會有所幫助:HTTP://stackoverflow.com/questions/1036019/does-calling-a-destructor-explicitly-destroy-an-object-completely – 2013-02-16 12:30:31

+0

考慮使用內存池或放置新/刪除,而不是這種優化。請記住:「不成熟的優化是萬惡之源」。 – doc 2013-02-16 13:08:02

回答

2
  • 默認構造函數調用所有成員變量的默認構造函數,不包括原始類型(char,int,pointers)。
  • 可以顯式調用析構函數,但它並不意味着對象的解除分配。如果對象在堆棧上,那麼它不可能對它做任何事情。
  • 析構函數在默認情況下不是虛擬的,但如果您計劃從類繼承,它們確實應該是虛擬的。
  • 如果對象被釋放(超出範圍,從堆中刪除或封閉對象被任何方式解析),將調用內部函數。
+0

我的意思是'虛擬'是每個班級默認都會得到一個(可以稱之爲),但它什麼都不做;我的意思並不是繼承:)。但是這是否意味着類的任何成員變量是結構體或類都將調用它們的析構函數? – Edward 2013-02-16 13:22:38

+0

@愛德華是的。總是隱含的。如果你真的爲你的課程寫了一個析構函數,你仍然不需要調用你的成員的描述符。你只需要關心'刪除'動態分配的對象,關閉打開的文件並釋放與你的對象相關的其他資源。如果你沒有這樣的東西,那麼你不需要寫一個析構函數。 – Notinlist 2013-02-16 13:46:07

+1

有關繼承的一點是誤導性的:使用非虛擬析構函數的基類是完全安全的。當析構函數應該是虛擬的唯一時間是當你打算在運行時多態性中使用基類的靜態類型時(換句話說,當你打算通過指針訪問具有動態類型「派生類」的實例時/(類型「基類」的非const)引用)。 – 2013-02-16 13:46:51

1

除了Notinlist的回答是:

默認構造函數調用基類的構造函數。

如果是這樣,不能析構函數本質上可以作爲一個「所有數據清除」 樣的功能,並通過 使代碼更高效一些地區清除動態內存分配的變量,並重新使用它 而不是讓計算機在 堆上找到新的內存塊?

你有點描述內存池。如果你願意,你的對象可以獲取內存緩衝區並從你創建的某個池系統返回內存緩衝區。但是,大多數情況下,分配速度足夠快並且不夠頻繁,以至於人們無法做到這一點。不是說他們很少,但他們需要發生很多注意到性能的影響。

2

我一直在研究它,我發現,如果我創建一個類與調用自己的析構函數它不會做的所有事情(即所有變量保持不變和實例仍然存在和功能可用)。

考慮以下代碼:

#include <iostream> 

struct A 
{ 
    ~A() 
    { 
     std::cout << "A::~A" << std::endl; 
    } 
}; 

struct B 
{ 
    A a; 
}; 

int main() 
{ 
    B b; 
    std::cout << "Calling b.~B()" << std::endl; 
    b.~B(); 
    std::cout << "Done" << std::endl; 
} 

You'll see,調用B的默認析構函數調用A的析構函數,因爲B包含A

Calling b.~B() 
A::~A 
Done 
A::~A 

只有當b超出範圍是堆棧展開並且合成B::~B()調用,並且依次爲01在他們的記憶被釋放之前,它們是。

+0

您的輸出與您的代碼(f()vs〜B())不匹配。 – Kleist 2013-02-16 13:05:59

+0

謝謝@Kleist,我簡化了代碼,但忘記更新輸出。 http://ideone.com/8fxvWQ顯示它按照說明運行。 – Johnsyweb 2013-02-16 13:09:57

1

手動調用析構函數通常是一個壞主意。關於destructors的C++ FAQ部分有很多關於這方面的很好的信息。

如果您確實想要明確銷燬對象,則可以使用其他作用域來安全調用析構函數(請參閱本FAQ entry)。此方法還可以防止使用已銷燬的對象實例。儘管該實例似乎可用,但實際上並非如此。

如果你的目的是要釋放一些,但不是全部,一個類的實例所擁有的資源,你可以嘗試兩件事情:

  • 定義在類clear()(或類似)的方法。
  • 確保clear()被稱爲後級的不變維持。

假設你最初的方法來手動調用析構函數的工作,或者你選擇這樣做,像上面的方法clear()的東西,在這兩種情況下,你以後可能會碰到的問題。

一個很好的理解和常練在C++中的資源管理的方法是資源獲取就是初始化(通常縮寫RAII,卻忽略了名字,如果它是混亂的,這個概念是可以理解的)。見this維基百科的文章或本answer有用的信息。

這裏是TL;博士雖然:

  • 資源的壽命應該總是被綁定到的 對象的壽命。
  • 對象的生命週期在構造函數完成時開始
  • 對象的生命週期在析構函數完成時結束。

遵循這個習慣用法通常會阻止C++資源管理問題發生。

+0

我仍然不確定爲什麼明確調用析構函數是一個壞主意。如果析構函數在刪除變量(如指針)(例如,如果(指針!= nullptr)刪除指針;)之前有保護,則肯定它不應該是有害的。這是否僅僅是爲了防止程序員忘記析構函數默認的東西和不被銷燬的東西(例如成員變量或將被析構函數調用的繼承類)? – Edward 2013-02-16 13:42:05

+0

@愛德華不,那是完全錯誤的。首先,那個守衛不會做任何事*,這沒用。其次,即使是有效的警衛也不會保護 - 即使是一個默認的析構函數也不能多次調用,因爲它會調用所有成員變量的析構函數(參見Notinlist的答案)。最後,你可以手動調用析構函數,但是你必須非常小心,因爲就像我說過的,它不能被重複調用析構函數,所以你必須防止任何後續的*自動*調用到它。 – 2013-02-16 13:50:35

相關問題