2017-06-22 56 views
-2

讓我們一個C++例如:C++ vtable究竟是如何工作的? (與實施例中的Q值。)

class A 
{ 
public: 
    A() { cout << "hey" << endl; } 
    ~A() { cout << "by" << endl; } 


}; 


class B : public A 
{ 
public: 
    B() {} 
    virtual ~B() { cout << "from b" << endl; } 

}; 
int main() 
{ 
    A * a = new B[5]; 



    delete[] a; 

    return 0; 
} 

此代碼的結果是「是」的無限循環,這是爲什麼? B虛表應該被upcast到沒有vtable的A,所以我希望它在嘗試訪問虛擬構造函數時拋出一個異常。

p.s. 在哪裏我可以閱讀各種有線構造函數析構函數的行爲舉例? (附範例)

+0

[何時使用虛擬析構函數?](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors) – CoryKramer

+1

@CoryKramer我不認爲這是完全重複的,OP不理解爲什麼只在派生類中創建虛擬驅動器不起作用。 – Slava

+0

歡迎來到Stack Overflow。請花些時間閱讀[The Tour](http://stackoverflow.com/tour),並參閱[幫助中心](http://stackoverflow.com/help/asking)中的資料,瞭解您可以在這裏問。 –

回答

3

將一個B的數組作爲A的數組刪除是未定義的行爲。這是真的,只要A有一個非平凡的析構函數(當它有一個微不足道的析構函數,它可能被定義,我不記得)。虛擬的繼承 - 無論在這種情況下它的不確定性都很重要。

之後的所有內容都比通過電子郵件發送到您的聯繫人列表中的硬盤映像文件和瀏覽器密碼緩存更好,這是C++標準下「未定義行爲」的合法示例。你好幸運啊。


特別是,我猜A是一個平凡的對象。並且delete[]正期望刪除一系列大小爲1的平凡物體。不知何故B是更大和不平凡的事實(它包含一個vtable)已經弄亂了你的編譯器,導致你的無限循環。

也許它存儲關於如何刪除數組的信息的格式與使用非虛擬析構函數的普通對象不同。用vtable的情況下,也許它在那裏存儲一個函數指針,而在平凡的情況下它存儲一個計數。

環路然後不是無限的,而是迭代(幾乎隨機32或64位號碼)倍,作爲指針值趨於相對隨機的。

如果你真的關心什麼具體的未定義行爲的代碼導致對這個特定的編譯器的這個特殊的系統在這個特別的版本,這個特殊的環境中,可以godbot你的代碼。但我不明白你爲什麼要關心。


C++的vtables僅用於一種類型的如果需要該類型創建。繼承和添加virtual不會改變類型確實或不需要vtable的事實。

C中的原始數組是而不是逆變或協變。如果你有一個數組,你不能安全它轉換成一個指向任何不同類型的(也有一些涉及到標準的佈局和原始字節/字符等的極窄的例外)。

如果我們回到你的例子並刪除陣列:

A * a = new B; 

delete a; 

這得到那麼糟糕。刪除a仍然是UB,因爲您正在刪除B作爲A

沒有virtual ~A()A中,您不能刪除B作爲A

爲了解決這個問題,我們添加:

virtual ~A() { cout << "by" << endl; } 

現在A有虛表 - 的A例如攜帶一個指向虛函數表,其中(除其他事項外)告訴編譯器如何刪除A或派生的A類型。

現在代碼是明確界定,並印刷

hey 
from b 
by 

如果我的頭,編譯器是正確的。

+0

我知道如何解決它,這是我不明白的有線終端的情況下,我知道它應該做的內存泄漏,但爲什麼會出現一個循環???爲什麼會發生? – joy

+0

@joy你做了未定義的行爲。這是第1段。那是「爲什麼有一個循環」。未定義的行爲意味着*任何事情都可能發生*。無限循環是「任何事物」的一個子集,所以它完全解釋了循環的「爲什麼」。現在,我還解釋了爲什麼編譯器可能會輸出一個看起來無限循環(它將一個指針解釋爲另一個指針)的可能性。什麼不清楚? – Yakk

0

這段代碼的結果是一個「by」的無限循環,爲什麼這樣?

因爲基類A沒有應該上溯造型到虛析構函數

乙虛函數表沒有虛函數表

它不工作的方式。當此代碼:

delete [] a; 

編譯,編譯器使用的classA定義,沒有虛函數表的。事實上,派生類有vtable在這裏並不重要。

所以我會希望它會拋出一個異常,當他試圖達到虛擬構造函數。

你的期望是錯誤的。首先,沒有虛擬構造函數這樣的東西。其次 - 在這種情況下,您使用A類(通過A*)。如果派生類有vtable或不在這種情況下無關緊要。

注意:C++語言不會規定虛擬函數解析必須通過vtable完成,儘管這樣實現它很常見。我用這個詞是因爲你提出了問題。