2016-05-25 422 views
0
#include <iostream> 

struct ABC{ 
    int A; 
    ABC(int i = 1) : A(i) {} 
    ~ABC() { 
     std::cout << A << std::endl; 
    } 
    void destruct() { 
     delete this; 
    } 
}; 

int main() { 
    ABC A1(2); 
    A1.destruct(); 
    return 0; 
} 

Output: 
2 
2 

我有這個代碼,我試圖手動刪除結構變量。這樣做,我意識到析構函數在這裏被調用兩次。這是爲什麼發生?爲什麼在調用destruct()時不會被刪除?爲什麼析構函數在這裏被調用兩次?

+2

從不「刪除」任何不是「新」的東西 –

回答

6

delete this致電原因undefined behaviour,這意味着什麼都可能發生。

delete僅可用於通過new創建的對象。


對於一個不平凡的析構函數自動物體(例如您A1),這是不可能的「早期消滅他們」,除非你也是在同一個地方創建另一個ABC範圍之前結束。換句話說,您不能「關閉」範圍結束時發生的銷燬過程。

+0

您可以通過強制早期超出範圍來觸發早期銷燬自動對象。你可以通過包裝一個聲明,初始化和使用{}中的自動對象的代碼塊來做到這一點。但是我不確定優化編譯器可能不會延遲銷燬,直到以後。 –

+0

@ P.Kouvarakis by「early」我的意思是「在其範圍結束之前」。 –

+0

是的,我明白了,我想我們都同意。我只是指出你可能比你想象的更早結束這個範圍。許多人傾向於僅使用{}爲循環,函數等定義代碼塊,並忘記他們可以使用它來定義變量作用域。 –

2

這是RAII的工作,再加上你的對象上自殺。在對象上調用析構函數幾乎總是錯的!並且調用析構函數兩次總是錯誤的,因爲它調用未定義的行爲。

你必須明白,C++正在處理內存給你,如果你只是讓它:

struct Foo{}; 
int main() { 
    Foo f;    // automatic storage, gets destroyed 
         // when object gets out of scope 

    Foo* g = new Foo(); // heap allocated 
    delete g;   // only here you have to delete 
} 

只要記住:不要delete任何您沒有通過new(感謝創造邁克藤爲評論)。除非需要,否則不要使用(裸)堆分配。

+0

'delete'做了兩件事:調用析構函數,*和*釋放內存。可以在不釋放內存的情況下調用析構函數(這也會導致UB出於釋放內存的不同原因) –

+0

@ M.M感謝您指出了這一點。我知道我過於簡單化了一下。我改變了「...在這裏你必須調用析構函數」,「...在這裏你必須刪除」,使它有點錯誤;) – user463035818

2

爲什麼當調用destruct()時[我的對象]不會被刪除?

當一個對象被C++破壞時,它並不意味着該對象消失。這意味着,從析構函數清理代碼得到執行,並從該點到對象的成員的任何訪問是無效的。

當你調用destruct()通過調用delete嘗試免費對象的內存。這本身是未定義的行爲,因爲您尚未分配帶有new的對象。此呼叫會導致首次打印。

然而,由於你的對象是自動記憶,C++需要調用析構函數時,對象失控的範圍。這是導致第二次打印輸出的呼叫。

注:您可以通過動態內存分配A1解決您的代碼:

int main() { 
    ABC *A1 = new ABC(2); 
    A1->destruct(); 
    return 0; 
} 

現在你得到一個單一的打印輸出(demo)。但是,在成員函數中隱藏delete的做法值得懷疑。

+0

'刪除這個;'用於管理自己的對象的習慣用法引用計數 –

1

兩點考慮在這裏: - 當他們走出去的範圍爲棧對象

1)析構函數總是被調用。所以不必擔心它們的釋放。

2)您不能&不應該使用堆棧上分配的對象上的刪除。一般而言,只要不確定是否僅因刪除堆對象而導致執行此操作,並且在此之後未指向該對象,則不應使用delete this

相關問題