2011-06-15 68 views
5

從C++標準(ISO/IEC 14882:2003(E)),§12.5.4,約超載operator delete重載操作者刪除在基類

如果delete表達式與一元開始::運算符,解除分配函數的名稱在全局範圍內查找。否則,如果使用delete-expression來取消分配其靜態類型具有虛擬析構函數的類對象,則釋放函數是動態類型的虛擬析構函數(12.4)的定義中的查找找到的函數。否則,如果使用delete-expression來釋放類T或其數組的對象,則該對象的靜態和動態類型應該相同,並且在T的範圍內查找解除分配函數的名稱。如果此查找失敗找到名字,名字在全球範圍內查找。如果查找結果不明確或無法訪問,或者查找選擇了位置釋放函數,則該程序不合格。

§12.5.7也很有意思:

由於成員分配和釋放函數是靜態的,他們不能是虛的。 [注意:但是,當delete-expression的cast-expression引用類類型的對象時,因爲實際調用的釋放函數是在作爲對象的動態類型的類的範圍內查找的,所以如果析構函數是虛擬的,效果是一樣的。例如,

struct B { 
    virtual ˜B(); 
    void operator delete(void*, size_t); 
}; 
struct D : B { 
    void operator delete(void*); 
}; 
void f() 
{ 
    B* bp = new D; 
    delete bp; // uses D::operator delete(void*) 
} 

在這裏,存儲用於d類的非陣列目的通過d釋放::操作者刪除(),由於虛擬析構函數。]

看完這個,我想知道...

  • 這是標準的一部分,由所有主要的C++編譯器(MSVC++,GCC)完全支持?
  • 如果是這樣,他們是怎麼做到的?隱藏的虛擬功能? 「特殊」虛擬析構函數調用? RTTI?
  • 使用標準示例:如果f()和D :: operator delete()在單獨的EXE/DLL/DSO中定義,那麼會出現問題嗎? (假設一切都使用相同的編譯器進行編譯,當然)

§5.3.5.5也可能是相關的:

在第一種方式(刪除對象),如果靜態類型的操作數與其動態類型不同,靜態類型應該是操作數動態類型的基類,靜態類型應該具有虛擬析構函數或行爲未定義。在第二種選擇(刪除數組)中,如果要刪除的對象的動態類型與其靜態類型不同,則行爲是未定義的。

+0

我的猜測:他們走虛擬表。正如標準所說,這個類需要有一個虛擬析構函數以正確的方式工作。對於非重載版本的「delete」也是如此。 – Xeo 2011-06-15 09:14:39

+0

我試着回答,但看起來你真正想要的是讓別人查看GCC源代碼並向你報告。我想你可以自己做。 – littleadv 2011-06-15 09:45:39

回答

6

我對VC++ ABI瞭解不多,但Itanium ABI已有詳細記錄。

the name mangling scheme擡頭看,看到:

<ctor-dtor-name> ::= C1  # complete object constructor 
       ::= C2  # base object constructor 
       ::= C3  # complete object allocating constructor 
       ::= D0  # deleting destructor 
       ::= D1  # complete object destructor 
       ::= D2  # base object destructor 

有趣的是:D0 # deleting destructor,這意味着,即使delete是非虛擬的,因爲它是從虛析構函數調用時,它可以被認爲是虛擬的所有效果和目的。

+0

這很有趣,謝謝。我會將您的答案標記爲已接受,至少在有人提出更具體的x86答案之前。 – 2011-06-15 10:11:43

+1

@ e-t172:我不認爲你需要'x86'(處理器)特定的答案。這是一個高層次的決定,儘管它展示給ABI,但我嚴重懷疑VC++,gcc或clang會在後端實現特定的變通辦法。 – 2011-06-15 10:20:34

0

挖入彙編代碼後在由GCC 4.8發射

的GCC將產生兩個片的代碼(類,其析構函數是虛擬的):

One is assembly snippet#1 for {Destructor + Dealloc} 
The other is assembly snippet#2 for {Destructor only} 

而對於其析構函數是類不是虛擬的,調用解除分配函數指令將在您調用delete的地方生成。

(經過討論假設析構函數是虛擬的) 因此,對於下面的代碼:

delete C // This will be translate as call snippet#1 for the correct dynamic type 

如果你的代碼如下:

p->C::~C() // this will be translate to call snippet#2 

因此解除分配功能與虛擬結合在一起析構函數。 所以我認爲這將回答你的問題,關於deallocate函數如何實現像虛擬,但也是靜態的。