2010-10-27 124 views
6

我有這個簡單的C++代碼,但我不知道如何使用析構函數。C++析構函數代碼

class date { 

public: 
    int day; 
    date(int m) 
    { 
     day =m; 
    } 

    ~date(){ 
    cout << "I wish you have entered the year \n" << day;  
    } 
}; 


int main() 
{ 
    date ob2(12); 
    ob2.~date(); 
    cout << ob2.day; 
    return 0; 
} 

問題是,我應該在析構函數代碼中寫什麼,在調用析構函數之後,它會刪除「day」變量。 ???

+0

很多答案引用「堆棧」。如果您不確定堆棧是什麼,請閱讀以下網頁:http://web.archive.org/web/20071029040931/www.dirac.org/linux/gdb/02a-Memory_Layout_And_The_Stack.php – 2010-10-27 18:51:53

回答

1

你不應該明確地調用你的析構函數。

當你在棧上創建對象(像你這樣),你需要的是:

int main() 
{ 
    date ob2(12); 
    // ob2.day holds 12 
    return 0; // ob2's destructor will get called here, after which it's memory is freed 
} 

當你在堆上創建你的對象,你還挺需要delete類的析構函數被調用之前並且內存被釋放:

int main() 
{ 
    date* ob2 = new date(12); 
    // ob2->day holds 12 
    delete ob2; // ob2's destructor will get called here, after which it's memory is freed 
    return 0; // ob2 is invalid at this point. 
} 

(未按呼籲這最後的例子刪除會導致記憶力減退。)

這兩種方法都有各自的優點的d缺點。堆棧方式非常快,分配對象將佔用的內存並且不需要顯式刪除它,但堆棧空間有限,無法輕鬆,快速和乾淨地移動這些對象。

堆是這樣做的首選方式,但是當涉及到性能時,它分配緩慢,你必須處理指針。但是,對於對象的操作,你有更多的靈活性,進一步處理指針會更快,並且可以更好地控制對象的生命週期。

+2

「堆是做它的首選方式」 - 做*什麼*?存儲對象?因爲那會是錯誤的:堆棧絕對是在C++中執行該操作的首選方式。 – 2010-10-27 18:46:46

+0

使用庫並在它們之間傳遞對象時,最好將它們分配到堆上(重要的)。確定堆棧是快速和骯髒的,並且在某些情況下是有意義的。但是當你想要使用對象時,傳遞,移動,複製和引用計數,那麼堆棧就不適合他們。直到C++ 0x移動語義變得司空見慣,這是... – Marius 2010-10-27 19:01:38

+1

抱歉,但這是錯誤的。對於參數傳遞,使用(const)引用。對於返回值 - 只需複製。什麼壞事都會發生。見例如http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/庫不應該需要堆分配的參數。這將是一個**非常糟糕的庫設計。關於堆棧分配沒有什麼快速和骯髒的。沒關係; – 2010-10-27 19:20:11

9

您不需要明確調用析構函數。這是在對象ob2的範圍的末尾,即在功能的末尾自動完成的。

此外,由於對象具有自動存儲,所以它的存儲不必刪除。這也是在函數結束時自動完成的。

幾乎從不需要手動調用析構函數(僅適用於低級庫代碼),並且只有在使用new(當您使用指針時才需要獲取內存時才需要手動刪除內存)。

由於手動內存管理容易泄漏,現代C++代碼嘗試不顯式使用newdelete。當真的需要使用new時,使用所謂的「智能指針」而不是常規指針。

+0

這是很好的好吧,但我相信海報有一個更基本的問題。 – Marius 2010-10-27 19:07:39

+0

對第三段略作澄清:當使用新的位置時,顯式調用析構函數是可以接受的(雖然很醜並且經常可以避免)。 – Sydius 2010-10-27 19:07:39

+0

@Sydius:這就是爲什麼我寫「只在低級庫代碼」。我們不要通過拋出更高級的符號來進一步混淆OP。 – 2010-10-27 19:22:10

14

你很少需要明確調用析構函數。相反,析構函數在對象被銷燬時被調用。

對於像ob2一個對象,它是一個局部變量,它是當它超出範圍破壞:

int main() 
{ 
    date ob2(12); 

} // ob2.~date() is called here, automatically! 

如果動態地分配使用new的對象,當對象被銷燬它的析構函數被調用使用delete。如果您有一個靜態對象,則在程序終止時(如果程序正常終止),將調用其析構函數。除非您使用new動態創建某些內容,否則不需要進行任何明確的清理操作(例如,銷燬ob2時,其所有成員變量(包括day)都將被銷燬)。如果你動態地創建了一些東西,你需要確保它在完成時被破壞;最佳做法是使用所謂的「智能指針」來確保自動處理此清理。

0

在這種情況下,您的析構函數不需要刪除day變量。

您只需要調用內存中已刪除的新內容。

這裏是你的代碼是什麼樣子,如果你使用new和delete來觸發調用只有在你需要直接調用析構函數非常特殊的情況下,析構函數

class date { 

    public: int* day; 
    date(int m) { 
     day = new int; 
     *day = m; 
    } 

    ~date(){ 
     delete day; 
     cout << "now the destructor get's called explicitly"; 
    } 
}; 

int main() { 
    date *ob2 = new date(12); 
    delete ob2; 
    return 0; 
} 
+0

除了代碼在堆上分配內存以存儲整數之外,該樣式還有什麼問題? – 2010-10-27 18:38:05

+0

這就是我的意思。但是另外(正如我在下面發表的帖子中指出的那樣),在現代C++中,任何與new指針結合使用的'new'都會被棄用,因爲這會導致很多麻煩。當然適合初學者。 – 2010-10-27 18:43:13

+0

該代碼還有一個問題:沒有正確定義的複製構造函數和複製賦值運算符,這對初學者來說是一個非常常見的錯誤。 – 2010-10-27 18:49:05

2

。默認情況下,當您創建一個自動存儲的變量並且超出範圍時,或者當使用delete銷燬與new動態分配的對象時,系統將調用析構函數。

struct test { 
    test(int value) : value(value) {} 
    ~test() { std::cout << "~test: " << value << std::endl; } 
    int value; 
}; 
int main() 
{ 
    test t(1); 
    test *d = new t(2); 
    delete d;   // prints: ~test: 2 
}      // prints: ~test: 1 (t falls out of scope) 

爲了完整起見,(這應該不是一般使用)的語法來調用析構函數類似於方法。析構函數運行之後,存儲器不再是類型的對象(應爲原料存儲器處理):

int main() 
{ 
    test t(1); 
    t.~test();   // prints: ~test: 1 
         // after this instruction 't' is no longer a 'test' object 
    new (&t) test(2);  // recreate a new test object in place 
}      // test falls out of scope, prints: ~test: 2 

注:調用t析構函數之後,該內存位置不再是test,這就是娛樂的原因的對象藉助於的放置新

+0

分配內存的常用方式是使用malloc(或新的數組)。這是我用堆棧變量看到的第一個例子。順便說一句,在第一個例子中,它會是test * d = new test(2); – 2010-10-27 18:44:25

+0

@VJo:我開始寫一個帶有堆棧分配內存緩衝區的例子,但是在調用析構函數之前需要使用placement new *(小問題),並且沒有顯示對析構函數進行多次調用的潛在問題。新標準中還有另一個* auto *分配的例子:對聯合的成員放置較少的需求,它允許具有不平凡的構造/析構函數的對象。在這種情況下,用戶可以顯式調用聯合活動成員的構造函數和析構函數。 – 2010-10-27 19:57:09

0

儘管析構函數看起來像是你需要調用的東西,當你完成使用它的時候,你不需要使用它,但是你不應該這樣使用它。

析構函數是在對象超出範圍時自動調用的,也就是當計算機離開實例化對象的「花括號」時。在這種情況下,當您離開main()時。你不想自己打電話。

0

您可能會對此處未定義的行爲感到困惑。 C++標準沒有關於如果在析構函數運行後使用對象會發生什麼的規則,因爲這是未定義的行爲,因此實現可以做任何它喜歡的事情。通常情況下,編譯器設計人員對未定義的行爲不做任何特別的處理,所以會發生什麼是其他設計決策的人爲因素。 (這可能會導致真正奇怪的結果。)

因此,一旦運行析構函數,編譯器就沒有關於該對象的進一步義務。如果你不再提及它,那沒關係。如果你真的提到它,那就是未定義的行爲,從標準的角度來看,行爲並不重要,而且由於標準沒有說明什麼,所以大多數編譯器設計人員不會擔心程序的功能。

在這種情況下,最簡單的辦法是不要觸摸對象,因爲它不佔用資源,並且它的存儲空間被分配作爲啓動該功能的一部分,並且直到該功能纔會被回收退出。因此,數據成員的值將保持不變。當它讀取ob2.day時,編譯器自然要做的就是訪問內存位置。

像任何其他未定義行爲的例子一樣,結果可能會在情況發生變化時發生變化,但在這種情況下,它們可能不會。如果編譯器能夠捕獲更多未定義行爲併發出診斷信息,那麼編譯器將會很好,但編譯器不可能檢測到所有未定義的行爲(某些行爲發生在運行時),並且通常他們不檢查他們不認爲的行爲有可能。