2013-10-22 110 views
2

昨天我學到了非常寶貴的一課:遵循三條規則。爲什麼這個「三規則」失敗實際上失敗了?

我想我會學會更容易,但錯誤只出現在刪除語句。這裏是場景:

foo.h 
    class foo{ 
    public: 
     ... 
     foo(int *Y); 
     ~foo(); 
     int *X; 
    } 
foo.cpp 
    ... 
    (.. constructor sets X to Y..) 
    foo:~foo(){ 
     delete [] X; 
    } 

main.cpp 
    vector<Foo> fooVec; 
    { // this is just to demonstrate scope problems more easily. 
     Y = new int[10000]; 
     (...init Y...) 
     fooVec.push(Foo(Y)) // I get it: this calls copy constructor, violating rule of three 
     (...forget to delete Y...) 
    } 
    // Y is now out of scope so this is a memory leak 
    cout << fooVec[0].[X][0] << " " << fooVec[0].[X][1] // THIS WORKS AS INTENDED DUE TO MEMORY LEAK 
    // program ends, fooVec goes out of scope 

哪些炸彈與"pointer being freed has not been allocated"。我追蹤回到fooVec超出範圍的地步,這個範圍調用了foos析構函數,它試圖刪除X.我的主要問題是:爲什麼delete實際上會失敗?我從來沒有刪除代碼中的Y(我得到這是一個內存泄漏),所以我實際上並沒有刪除一個指針。此外,內存顯然存在,因爲cout行的作品。如果這個行失敗了,我本可以早點想出問題的。

下面的評論似乎表明「Foo(Y)超出範圍時,X被刪除」。但是,如果是這種情況,爲什麼地球上cout聲明有效?

注:我沒有複製構造函數和賦值超載,因此「三失敗規則」。我得到了我應該擁有的矢量push_back語句。我在問爲什麼不讓他們在這裏殺了我,因爲我忘了釋放Y,所以我實際上並沒有刪除一個指針兩次。

編輯:

謝謝大家的幫助。 user1158692s的回答總結出來了,但是在thelambs回答中的所有評論也幫助我弄清楚究竟發生了什麼,他們一直在爲我回答許多問題。如果我能會同時接受..

+0

我只看到一個析構函數。你的複製構造函數和(複製)指派操作符在哪裏? – peppe

+0

我沒有他們,因此,「三次失敗的規則」。我的問題是,爲什麼不讓他們在這裏殺了我? – Tommy

+0

您將獲得默認的複製構造函數和賦值運算符。哪個會複製指針。現在你有兩個使用相同指針的對象。第一次刪除作品。第二個刪除已被刪除的指針。 –

回答

5

[注:newFoo是原帖的部分不再,它指的是物體推入載體fooVec,這是目前就地完成:foovec.push_back(Foo(Y))]

does雙刪除。首先,當newFoo超出範圍時,它確實是delete[] x(這是第一次刪除)。您在這裏寫了forget to delete Y,但Y事實上被newFoo的內容刪除了。

第二次刪除是當newFoo的副本被刪除(fooVec超出範圍時)。 newFoo的副本也是delete[] x,並且由於您沒有複製構造函數,所以xnewFoonewFoo的副本相同,因此它是雙重刪除。

現在,您將無法輕鬆解決此問題。由於在你要寫的拷貝構造函數中,你不知道如何拷貝x(它有多少個元素?1?100000?)。

+0

但是,如果newFoo刪除X,爲什麼地球上的'cout'聲明會按預期工作? – Tommy

+0

我編輯了我的代碼。我從來沒有明確創建newfOO:不知道這是否會改變任何東西。 – Tommy

+0

未定義的行爲。它可以做任何事情。 – Simple

3

我不確定您的意思是「按預期工作」 評論。代碼中沒有內存泄漏,而是相同內存的雙重刪除 。在 定義的範圍末尾,調用newFoo的析構函數;此 又會刪除在Y = new int[10000], 處分配的內存,並使fooVec中的Foo對象中的指針無效。 您的未定義行爲在那一刻開始,因爲fooVec 現在包含一個正式不可複製的對象。在 之後,任何事情都可能發生:未定義就是這樣,未定義。

你去訪問內存(未定義的行爲),並 刪除它第二次在對象的析構函數中 fooVec(未定義行爲)。

+0

「按預期工作」意思是打印在Y中初始化的值。 – Tommy

+1

@Tommy這是未定義行爲的一種可能結果。當然,不是唯一的,在不同的情況下,你的代碼可能會做不同的事情。 –

2

如果沒有顯式拷貝構造函數,則複製X中的指針值。這裏的所發生的事情你:

  • 實例化newFoo持有指針值Y
  • 推newfoo到向量,向量現在持有另一FOO這也有一個指針值Y
  • newFoo超出範圍,析構函數刪除內存值Y指出
  • COUT採用指針值Y,但刪除記憶還沒有被覆蓋它「作品」
  • fooVec超出範圍,它擁有嘗試刪除內存foo的析構函數點編號由Y
  • BANG
+0

newFoo應該更改爲Foo(Y)。關於這一點,但我從來沒有實例化newFoo,我只是在push_back語句中隱式地做了它。對不起再次改變我的問題。 – Tommy

+0

@Tommy你的編輯並沒有改變任何關於我的答案的東西,只是用'pushback()'調用中創建的臨時foo替換'newfoo'。臨時對象超出範圍並刪除Y所指向的內存。 –

+0

另請注意,只要存儲器耗盡,矢量將對其所有對象執行額外的複製構造函數/析構函數調用。所以,即使你泄漏了所有的對象,並且避免了創建會刪除數組的臨時對象,vector'>'仍然可以自行觸發相同類型的崩潰。 – cmaster