2011-01-29 53 views
30

當我不申報例如constructor,編譯器會爲我提供了一個default constructor,將有沒有參數和定義(身體),因此,將採取任何行動C++默認的析構函數

如果我現在沒有聲明destructor,編譯器將提供給我,沒有確定指標(體)一default destructor,因此,我認爲沒有動作

所以,如果我跟例如對象完成,不會在default destructor再分配(免費)內存使用的對象?如果沒有,我們爲什麼要得到它?

而且,也許同樣的問題適用於default constructor。如果它什麼都不做,爲什麼它默認爲我們創建?

謝謝。

+19

構造不分配對象的存儲器,所述存儲器已經被分配給它以某種方式在構造函數被調用之前。 – dreamlax 2011-01-29 13:58:21

+0

提出這個問題時有太多錯誤的陳述。指出他們的答案是不被接受的。 – Antonio 2017-10-26 17:07:06

回答

0

默認的析構函數就沒有知道什麼記憶類「擁有」的方式,能夠釋放它。

至於默認的構造函數部分,我將引用Wikipedia article在這一個...

在C++中,因爲他們是 在某些情況下 自動調用默認的構造函數有顯著:

  • 當一個對象值被聲明爲沒有參數列表時,例如MyClass x ;;或者在沒有參數列表的情況下動態分配,例如,新的MyClass;所述 默認構造用於 初始化對象
  • 當對象的數組被聲明,例如MyClass x [10] ;;或動態分配的 。新 MyClass [10];默認構造函數 用於初始化所有
  • 當一個派生類構造函數不顯式調用它的初始化 列表基 類的構造函數,用於 基類的默認構造函數被調用
  • 當一個類的元素構造函數不顯式調用它的價值對象字段 一個構造函數在其 初始化列表,默認 構造函數字段的類是 叫
  • 在標準庫中,某些容器「中填入」使用值 默認構造函數,當值爲 時未明確給出,例如 vector(10);使用10個元素初始化 向量,這些元素是 ,其中填充了我們類型的缺省構造的 值。

在上面 的情況下,這是一個錯誤,如果 類沒有默認的構造函數 。編譯器將 隱式地定義一個默認的構造函數

如果沒有構造函數 明確的一類定義。隱含聲明的默認 構造函數的這個 相當於用空白體定義的默認構造函數 。 (注:如果某些構造函數是 定義,但它們都是非默認, 編譯器不會隱式 定義默認構造函數這 意味着一個默認的構造可能 不是一類存在。)

4

因爲如果您沒有任何(publiclly-accessible)構造函數或析構函數,那麼該類的對象不能實例化。試想一下:

class A 
{ 
private: 
    A() {} 
    ~A() {} 
}; 

A a; // Oh dear! Compilation error 

如果不明確聲明任何構造函數和析構函數,編譯器必須提供一個允許對象的創建。

+1

爲什麼編譯錯誤?沒有默認的構造函數嗎?謝謝 – Simplicity 2011-01-29 16:09:00

1

簡而言之,在C++中,每個對象都需要一個構造函數和一個析構函數,即使它們沒有做任何事情。所以在後臺爲你創建它們的編譯器可以滿足這個要求。

較長的答案是構造函數負責類的成員的初始化。默認的構造函數會對所有成員進行默認初始化。 (這對POD類型並不意味着什麼,但是其他類會得到它們的默認構造函數。)

4

默認的析構函數不會執行任何操作(就像默認的構造函數一樣)。

如果你的析構函數實際需要做些事情(例如:釋放一些資源),你需要自己定義一個。請注意,通常您應該遵循rule of three:如果您的程序需要在其析構函數中執行某些操作(例如釋放資源),則還應提供複製構造函數和賦值運算符; C++還提供了它們的默認版本(它們也不會做任何事情)。

當您處理不需要執行任何操作的簡單類時,默認的構造函數/析構函數/賦值運算符/複製構造函數非常有用。一個特例是POD:它們(在C++ 0x之前)甚至不能有顯式的構造函數或析構函數。

3

默認的構造函數和析構函數僅僅是一種商品,以防您不需要手動編寫空白版本就可以不需要特別處理類。這對其他面向對象的語言來說很常見,例如在Java中,如果成員的零初始化足夠,您不需要提供構造函數。同時,它需要與C向後兼容。如果在C中有一個struct,它將不具有構造函數或析構函數(C沒有這些概念),以便能夠在C++中處理該代碼,該代碼必須是有效的代碼。

另一種選擇是在語言中聲明一個類可以沒有構造器或析構函數,但是然後整個語言規範將不得不處理這樣的事實,即某些類型可能具有構造函數和析構函數,這將使語言更加複雜和難以指定。雖然具有隱式定義的版本不會改變行爲並簡化規範。

在此級別上,它類似於應用於基類中方法的術語覆蓋。在基類中,並不是覆蓋什麼,沒有什麼可以重寫的!然而,該語言明確指出,在基礎中聲明的虛擬非純方法是一個覆蓋。這使得規範簡單地說,當通過指針或引用調用方法時,將調用最終覆蓋,而無需添加extre *或基本方法實現,如果該特定層次結構中該特定方法不存在覆蓋。

49

說編譯器生成的默認構造函數不採取任何行動是錯誤的。它相當於一個用戶定義的構造函數,它具有空的主體和一個空的初始化列表,但這並不意味着它不會採取任何行動。這是它的作用:

  1. 它調用基類的默認構造函數。
  2. 它初始化vtable指針,如果類是多態的。
  3. 它調用擁有它們的所有成員的默認構造函數。如果存在具有一些構造函數的成員,但沒有缺省構造函數,那麼這是編譯時錯誤。

只有一個類不是多態時,沒有基類,也沒有需要構造的成員,那麼編譯器生成的默認構造函數什麼都不做。但即使如此,默認的構造函數有時也是需要解釋其他答案的原因。

析構函數也是如此 - 它調用基類的析構函數和所有具有它們的成員的析構函數,所以在一般情況下,編譯器生成的析構函數什麼都不會執行。

但內存分配的確與這無關。內存是在調用構造函數之前分配的,並且只有在最後的析構函數完成後才釋放它。

0

使用智能指針時,默認的析構函數(請參閱Sergey的答案)對於避免內存泄漏非常關鍵。這裏的一個示例:

#include <iostream> 
#include <memory> 

using namespace std; 

class Foo { 
public: 
    Foo(int n = 0): n(n) { cout << "Foo(" << n << ")" << endl; } 
    ~Foo() { cout << "~Foo(" << n << ")" << endl; } 
private: 
    int n; 
}; 

// notes: 
// * default destructor of Bar calls destructors of unique_ptr<Foo> foo 
// and of unique_ptr<Foo[]> foo3, which, in turn, delete the Foo objects 
// * foo2's Foo object leaks 
class Bar { 
public: 
    Bar(): foo(new Foo(1)), foo2(new Foo(2)), foo3(new Foo[2]) { } 
private: 
    unique_ptr<Foo> foo; 
    Foo* foo2; 
    unique_ptr<Foo[]> foo3; 
}; 

int main() { 
    Bar bar; 
    cout << "in main()" << endl; 
} 

這裏的輸出,顯示出一個泄漏發生僅foo2

Foo(1) 
Foo(2) 
Foo(0) 
Foo(0) 
in main() 
~Foo(0) 
~Foo(0) 
~Foo(1)