1. 程式人生 > >C++中的"delete this"

C++中的"delete this"

引子

  • C++中delete能否用於棧物件
  • C++中”delete this”會出現什麼問題
  • C++中”delete this”的注意事項

C++中delete能否用於棧物件

在大家的印象中delete只能用於new出動態記憶體物件,我們使用了g++/Clang++/MSVC來測試delete棧物件的情況。

測試程式碼如下:

int main()
{
    int num = 10;
    int* p = #
    delete p;
    cout << num << endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

使用g++/Clang++/MSVC進行編譯都是沒有問題的(肯定啊,從詞法語法甚至到語義分析都沒有問題)。只是在執行結果時稍有不同,g++編譯執行會輸出10。Clang++和MSVC編譯執行時會直接down掉。經過實驗室delete棧物件是未定義的行為。

C++中”delete this”會出現什麼問題

經過以上得知delete棧記憶體是未定義的行為,但是delete動態記憶體上的”this”指標呢?我們同樣在3個編譯平臺上進行測試。測試原始碼如下:

class A
{
public:
    int num;
    A() { num = 10; }
    void del() 
    { 
        void* ptr = this;
        delete this; 
        // 繼續執行成員函式
        print();
    }
    void print() { cout << num << endl; }
};

int main()
{
    A* a = new A();
    a->del();
    return 0;
}

在g++上編譯執行輸出結果隨機,而在Clang++上編譯執行輸出結果”10”,在Visual Studio上編譯執行輸出結果恆為”-17891602”。說明delete this操作編譯通過,但是是未定義的行為,有的編譯平臺在delete this後會修改原有位置的資料,有的編譯平臺只是將相關記憶體位置設為”delete”,其實這是和編譯平臺所實現的執行時庫相關。

由於是未定義行為,所以delete this還是比較危險的行為,當類定義了虛擬函式或者有其他執行緒使用了這一塊記憶體時,會導致嚴重的危害甚至程式崩潰

這裡有必要說明一下不同的編譯平臺在delete操作後對原有記憶體的行為。

  • 0xABABABAB : Used by Microsoft’s HeapAlloc() to mark “no man’s land” guard bytes after allocated heap memory
  • 0xABADCAFE : A startup to this value to initialize all free memory to catch errant pointers
  • 0xBAADF00D : Used by Microsoft’s LocalAlloc(LMEM_FIXED) to mark uninitialised allocated heap memory
  • 0xBADCAB1E : Error Code returned to the Microsoft eVC debugger when connection is severed to the debugger
  • 0xBEEFCACE : Used by Microsoft .NET as a magic number in resource files
  • 0xCCCCCCCC : Used by Microsoft’s C++ debugging runtime library to mark uninitialised stack memory
  • 0xCDCDCDCD : Used by Microsoft’s C++ debugging runtime library to mark uninitialised heap memory
  • 0xDEADDEAD : A Microsoft Windows STOP Error code used when the user manually initiates the crash.
  • 0xFDFDFDFD : Used by Microsoft’s C++ debugging heap to mark “no man’s land” guard bytes before and after allocated heap memory
  • 0xFEEEFEEE : Used by Microsoft’s HeapFree() to mark freed heap memory. —[Visual Studio除錯過程中相關位元組含義]

在Visual Studio上delete後會將原有記憶體設定為”0xfeeefeee”,具體結果如下圖所示:
刪除前this處記憶體內容為10,其中後面”0xfdfdfdfd”是Visual Studio在DEBUG模式下在堆上的守衛位元組
刪除前

The 0xfeeefeee bit pattern indicates that this memory is part of a deallocated memory allocation (free() or delete). If you are seeing the 0xfeeefeee bit pattern it means that you are reading memory that has been deallocated by free() or delete.—[0xfeeefeee]

The 0xfdfdfdfd bit pattern is the guard pattern around memory allocations allocated with the “non-aligned” (default) allocators.Memory allocated with malloc(), realloc(), new and new [] are provided with a guard block before and after the memory allocation. When this happens with an non-aligned (default) memory allocator, the bit pattern used in the guard block is 0xfdfdfdfd.—[0xfdfdfdfd]

The 0xabababab bit pattern is the bit pattern for the guard block after memory allocated using HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED) or CoTaskMemAlloc().If you are seeing the 0xabababab bit pattern it means that you are reading memory after a memory block that has been allocated by HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED) or CoTaskMemAlloc().—[0xabababab]

delete this後,原有內容設定為”0xfeeefeee”表示為已釋放的記憶體。

這裡寫圖片描述

在g++下刪除前和刪除後,this值如下圖所示:

g++

g++也使用了類似於Visual Studio的模式對堆上分配的記憶體有“守衛位元組”,只是g++使用的“守衛位元組”是”0xabababab”和Visual Studio的守衛位元組相同,表示空閒記憶體的位元組為”0xfeeefeee”,表示已釋放的位元組是”0x007000c0”。

其實這些位元組有一個專門的名字”Magic debug values“,維基百科上有詳細的說明,我就不當搬運工了。

Magic debug values are specific values written to memory during allocation or deallocation, so that it will later be possible to tell whether or not they have become corrupted, and to make it obvious when values taken from uninitialized memory are being used.—[Magic debug values]

C++中”delete this”的注意事項

  • 你必須100%絕對確保”this”物件是通過new分配的(不是通過new[],不是placement new,不是棧上的區域性物件,不是全域性物件,不是另一個物件的資料成員;僅僅只是通過原始的new運算子)
  • 你必須100%絕對確保呼叫”delete this”操作的成員函式是最後呼叫的成員函式
  • 你必須100%絕對確保在當前函式中”delete this”後,呼叫的其他成員函式不會讀取”this”物件。
  • 你必須100%確保再也不會使用”this”指標。即使你使用this指標和其他指標比較,例如nullptr,列印this指標,轉換this指標等等。