2017-10-10 103 views
3

我有一個帶有指向整數的指針的類。當變量仍然活着時被調用的C++析構函數

然後一個靜態函數將返回該整數的值。

我注意到當調用靜態函數時,每次都會爲該對象調用析構函數。

我不明白爲什麼會發生這種行爲。

class Dog 
{ 
public: 

Dog(int val){ 
this->pVal = new int(val); 
} 

~Dog(){ 
delete this->pVal; 
} 

static int GetVal(Dog d){ 
    return *(d.pVal); 
} 

int *pVal; 
}; 

這是班級。

這是我的測試驅動程序代碼。

Dog fido(20); 
std::cout << Dog::GetVal(fido); //20 and destructor for fido called 
Dog rex(21); 
std::cout << Dog::GetVal(fido); //21 but should be 20 
std::cout << Dog::GetVal(rex); // should be 21 

我注意到兩個狗對象都住在不同的內存地址,但是int指針居住在同一個地址。我相信這是因爲調用GetVal時會調用fido的析構函數,但我不知道爲什麼會出現這種行爲。

+0

如果你使用了unique_ptr而不是手動管理生命週期,那麼你的編譯器向你顯示了你的錯誤(代碼不會編譯)。 – 2017-10-10 21:10:49

+1

僅僅因爲對另一個問題的回答適用於此問題,並不會使此問題重複。這個問題是通過證明問題而提出的; 「重複」詢問特定主題。除非你已經知道這個問題代碼的問題,否則你不會知道所謂的重複是相關的。 – Darryl

+0

我同意上面的Darryl的評論。投票重新開放。 – dasblinkenlight

回答

9

雖然「Fido」的析構函數確實被調用,但這不是原始的「Fido」,而是它的副本。您的GetVal(Dog d)函數按值取值爲Dog,這意味着「Fido」在傳遞到GetVal之前被複制,然後在完成時銷燬副本。

傳遞Dog&通過const參考修復了這個問題:

static int GetVal(const Dog& d){ 
    return *(d.pVal); 
} 

Demo.

注:上面沒有解釋爲什麼你21 「菲多」。由於您沒有定義複製構造函數或賦值運算符,因此編譯器會爲您生成一個簡單的複製構造函數。因此,「Fido」副本中的pVal指向與原始「Fido」中的pVal相同的位置。一旦從Dog::GetVal(fido)返回時拷貝被破壞,內存就有資格重用,並且原始「Fido」內的指針變爲懸空。當您第二次調用Dog::GetVal(fido)時,該函數通過取消引用懸掛指針而導致未定義的行爲。第二次調用打印21這一事實,即您傳遞給「Rex」的構造函數的值,強烈表明在銷燬「Fido」副本時釋放的內存正在構建「Rex」時立即重用。但是,C++沒有這樣做的要求。如果發生這種情況,當「Rex」和「Fido」在運行結束時被銷燬時,代碼會第二次產生UB。

+0

非常感謝你。我沒有意識到創建了一個副本。 – mocode10

+0

@ mocode10你應該遵循一些規則來防止這樣的問題。具體而言,五或規則爲零:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-five,https://github.com/isocpp/CppCoreGuidelines/blob/master /CppCoreGuidelines.md#Rc-zero – Jens

+0

三條規則:https:// stackoverflow。com/q/4172722/14065 –

相關問題