2011-08-26 50 views
9

我是新來的,所以我很容易:) 從我的講師早些時候講的話來看,虛擬表的順序很重要。 但我不明白這個原因!!?虛擬表的順序是否重要?

由於下面的代碼:

class A 
{ 
public: 
    A() {cout <<"1" << endl;}; 
    A (const A& s) {cout << "2" << endl;} 
    ~A() {cout << "3" << endl;} 
    void f1() {cout << "4" << endl; f2();} 
    virtual void f2() = 0; 
    virtual void f3() {cout << "5" << endl;} 

}; 


class B : public A 
{ 
public: 
    B() {cout << "6" << endl;} 
    B(const B& b) : A(b) {cout << "7" << endl;} 
    ~B() {cout << "8" << endl;} 

    virtual void f1() {cout<<"9"<<endl;} 
    void f2() {cout<<"lO"<<endl; f4();} 
    virtual void f2(int i) {cout << "11" << endl;} 
    virtual void f4() {cout << "12" << endl; f3();} 

}; 

他說,順序是:

A's vtable : 
A::f2() 
A::f3() 

B's vtable : 
B::f2() 
A::f3() 
B::f1() 
B::f2(int) 
B::f4() 

但我不明白爲什麼它是重要的?他說,如果這個虛擬表是 不是正確的順序,你能解釋一下爲什麼嗎?

回答

8

vtable的順序對於事情正常工作很重要,但只對編譯器(即你不需要關心,因爲它處理它)很重要。

如果編譯器本身違背了它,那麼事情就會中斷,因爲函數是通過偏移量來查找的(所以偏移量會產生一個隨機函數,這將是災難性的)。但是一般的程序員並沒有這樣做,不需要擔心vtable在什麼順序。

1

該vtable是一個「查找」表。它基本上是一個指向班級虛擬功能的地圖。如果發生故障,指針會指向錯誤的功能。例如,如果您想撥打B:f1()(不採用參數),而是撥打B::f2(),則需要輸入int

16

在C++標準中沒有vtables的概念。只是大多數實現(如果不是全部的話)將其用於虛擬調度。然而,確切的約定完全是實現定義的。

這就是說......函數的順序很重要,但不是程序員,而是編譯器 - 你可以在你的代碼中安排你想要的功能。然而,編譯器通常會將每個函數指針放入vtable中的特定位置,它專用於該函數。所以當它需要調用f()時,它知道f()函數的索引並從vtable獲取該指針。

這個問題可以幫助你以及:Virtual dispatch implementation details

+0

非常感謝你!獻給你們所有人 ! –

+1

@ Ron_s請確保您單擊此答案左上角的複選標記,如果它回答您的問題。 –

2

V表的每一個客戶端需要知道正確的順序,使他們能夠找到調用正確的方法。但只要各方對訂單達成一致,那麼訂單是什麼並不重要。

5

只有當類聲明外部ABI的接口(例如COM/XPCOM)時,它纔是重要的。

大部分時間它並不重要,沒有理由關心它。

+1

+1外部abi是一個非常好的點 –

+2

+1在Windows平臺上,vtable佈局是標準的唯一原因是因爲COM。作爲一個編譯器廠商,如果你想支持COM,你必須遵循微軟的實現。但是,COM未使用的更高級功能(例如虛擬繼承或非接口的多重繼承)與實現相關。 –

0

我不知道他是什麼意思,但我會盡力解釋它是如何工作的:

首先,C++定義了名稱和簽名的方法。所以當C++啓動一個類的虛擬表時,它將用派生的具有相同名稱和簽名的虛函數替換所有基類的虛函數。

當一個類派生另一個類時,它實際上是建立在它上面的。因此,基類中存在的內存塊的一部分(複雜,讀到這裏 - Virtual inheritance

虛擬表只是持有「指針」這取決於在運行時類型正確的函數。