2010-04-30 139 views
7

當我有我的類的std :: vector時,我對如何使用析構函數感到困惑。C++析構函數與類對象的std :: vector的問題

所以,如果我創建一個簡單的類,如下所示:

class Test 
{ 
private: 
int *big; 

public: 
Test() 
{ 
    big = new int[10000]; 
} 

    ~Test() 
{ 
    delete [] big; 
} 
}; 

然後在我的主要功能我做到以下幾點:

Test tObj = Test(); 
vector<Test> tVec; 
tVec.push_back(tObj); 

我在測試的析構函數得到一個運行崩潰時,我超出範圍。爲什麼是這樣的,我怎樣才能安全地釋放我的記憶?

+1

不知何故,我的DevC++從這些chashes中「拯救」了我。此代碼[http://www.ideone.com/EHZBV]在Windows上的DevC++上執行得非常好。任何想法爲什麼可能會發生?不是很好。當它應該崩潰時,我希望它崩潰! – Lazer 2010-05-01 04:35:25

回答

15

你的問題是在這裏:

Test tObj = Test(); 

Test()創建一個臨時Test對象,然後被複制到tObj。此時,tObj和臨時對象都將big設置爲指向該數組。然後臨時對象被銷燬,調用析構函數並銷燬數組。所以當tObj被破壞時,它會試圖再次銷燬已經毀壞的數組。

此外,當tVec被銷燬時,它會破壞它的元素,所以已經被破壞的數組將被再次銷燬。

您應該定義一個拷貝構造函數和賦值運算符,這樣,當一個Test對象被複制時,big數組被複制,或者有某種形式的引用計數,這樣它不會被破壞,直到所有的業主都毀滅了。

一個簡單的辦法是定義你的類是這樣的:

class Test 
{ 
private: 
std::vector<int> big; 

public: 
Test(): big(10000) {} 
}; 

在這種情況下,你就不需要定義任何析構函數,拷貝構造函數或賦值操作符,因爲std::vector<>成員會照顧一切。 (但是請注意,這意味着每當複製Test的實例時,都會分配和複製10,000個字節。)

+0

原則上正確,但不詳細。 Test tObj = Test()不會創建臨時對象。 複製構造函數在tVec.push_back(tObj)處被調用; – shura 2010-04-30 15:38:08

+2

@shura:根據定義,C++中的'Test()'表達式創建一個臨時對象。編譯器可以優化它,但通常情況下會創建臨時文件。 – AnT 2010-04-30 15:56:43

+0

@AndreyT:我不明白你的'按照定義'。你說自己編譯器可以優化它。他們這樣做。定義是否突破了那個點?你使用哪個編譯器調用Test o = Test()的拷貝構造函數? – shura 2010-05-04 12:27:37

21

問題是您沒有爲Test定義複製構造函數。所以編譯器爲你生成一個默認的拷貝構造函數,它只是複製對象的內容 - 在這種情況下是int指針。

現在,當您將對象推回到矢量中時,它會與複製構造函數一起隱式複製。這導致兩個對象指向相同的整數數組!所以最後,兩個析構函數試圖刪除相同的數組 - BANG。

無論何時定義該公司擁有通過指針成員*,除了析構函數,你必須定義一個拷貝構造函數爲它的類。 更新:和賦值運算符,出於同樣的原因(感謝@詹姆斯:-)

UPDATE2:一個不重要的方式來解決所有這些限制是定義一個靜態數組而不是動態分配一個:

class Test 
{ 
private: 
    int big[10000]; 
    // no need for constructors, destructor or assignment operator 
}; 

但是,最好的做法是使用std::vector<int>而不是數組。

*也就是說,包含指向與所有權語義成員(感謝@Steve傑索普澄清)

+0

沒錯。當測試對象被添加到矢量時,它將在複製構造函數的幫助下被複制。如果您嘗試訪問矢量對象的數組,也應該崩潰。 – MatsT 2010-04-30 14:56:03

+0

「每當你定義一個包含指針成員的類」 - 擁有所有權語義。沒有所有權語義的指針不需要類的特殊處理,但當然,調用者必須確保只要對象可能使用它,referand仍然有效。 – 2010-04-30 15:19:01

+0

@Steve,對我來說遏制意味着所有權,但你說得對,我現在試圖說清楚。 – 2010-04-30 19:04:43

0

沒有複製構造函數,矢量將創建對象的平面副本。這導致Test類型的兩個對象引用相同的數組big。第一個實例刪除數組,當它被破壞時,然後第二個實例試圖解引用已刪除的指針,這是未定義的行爲。

0
Test tObj = Test(); 

這是錯誤的,應該是因爲它不創建副本:

Test tObj; 

這也創造了很多的副本:

vector<Test> tVec; 
tVec.push_back(tObj); 

因此,如果您免費一個int數組,你將釋放所有的數組。以下刪除將失敗。

你需要的是兩種:

  • 使用拷貝構造函數來爲每個類單獨陣列

  • 爲什麼使用指針?

class Test  
{ 
private: 
    int big[10000];  
public: 

}; 

這將正常工作。

+0

你的建議通常可以正常工作,但有些平臺在堆棧上放置int [10000]'會導致問題。從堆中分配大對象通常更安全。 – 2010-04-30 15:08:43

+0

很難確切知道原始海報使用哪個平臺,但是如果他真的知道爲什麼他必須在這種情況下使用堆分配,也許他不會犯他以下的錯誤。 嗯,我不知道,也許他應該只使用std :: vector? 當然,您可以創建(智能)測試指針以避免所有不必要的副本 – Nikko 2010-04-30 15:48:26

相關問題