2016-03-08 18 views
1

考慮下面的代碼。在C++中如何確定虛擬表中的索引?

#include<iostream> 

using namespace std; 

class Base 
{ 
public: 
    virtual void function1() {cout<<"Base:function1()\n";}; 
    virtual void function2() {cout<<"Base:function1()\n";}; 
}; 

class D1: public Base 
{ 
public: 
    virtual void function1() {cout<<"D1:function1()\n";}; 
    virtual void function2() {cout<<"D1:function2()\n";}; 
}; 


int main() 
{ 
    Base *ptr= new D1; 

    ptr->function1(); 
    ptr->function2(); 

    return 0; 
} 

ptr將指向D1 obj。因此無論何時我調用ptr-> function1(),函數地址都是從D1類的虛擬表中提取的。它同樣適用於ptr-> function2()。在這種情況下,vtable [0]將具有指向function1()的函數指針,vtable [1]將具有指向function2()的函數指針。

我的問題是如何函數調用vtable索引映射發生?

ptr-> function1()& ptr-> function2()分別索引到vtable [0] & vtable [1]?

+0

請參閱:http://stackoverflow.com/questions/70682/what-is-the-vtable-layout-and-vtable-pointer-location-in-c-objects-in-gcc-3-x – vcp

+0

編譯器在某些時候決定並注意到其內部的數據結構,就像其他的一樣。通常,算法只應該依賴於類的定義,因此在每個TU中,除了每個TU外,它不需要任何東西來產生相同的vtable。 –

+0

編譯過程中發生這種情況。編譯器知道這些函數是虛擬的以及如何調用它們。 – molbdnilo

回答

1

我稱之爲優秀,細節blog post「在計算機科學中的所有問題都可以通過間接的另一個層面得到解決」,並在下面試圖解釋使用虛函數表爲您簡單例。看看你是否閱讀徹底。

struct Base; 
// enumerates all virtual functions of A  
struct table_Base { 
    void (*function1)(struct Base *this); 
    void (*function2)(struct Base *this); 
}; 

struct Base { 
    const struct table_Base *pvtable; // table maintains pointers to virtual functions. Eventually to implementations 
    int data; 
}; 

void Base_function1(struct Base *this) { 
    std::cout << "Base:function1()" << std::endl; 
} 

void Base_function2(struct Base *this) { 
    std::cout << "Base:function2()" << std::endl; 
} 

// table data for Base 
static const struct table_Base table_Base_for_Base = { Base_function1, Base_function2}; 

void Base_Ctor(struct Base *this) {  
    this->pvtable = &table_Base_for_Base; 
    this->data = 1; 
} 

// Now for class D1 
struct D1;  
struct table_D1 { 
    void (*function1)(struct D1 *this); 
    void (*function2)(struct D1 *this); 
}; 

struct D1 { 
    struct Base base; 
    const struct table_D1 *pvtable; 
    int more_data; 
}; 

void D1_function1(struct D1 *this) { 
    std::cout << "D1:function1()" << std::endl; 
} 

void D1_function2(struct D1 *this) { 
    std::cout << "D1:function2()" << std::endl; 
} 

// Important functions that do re-direction 
void D1Base_function1(struct Base *this) { 
    D1_function1((struct D1*) this); 
} 

void D1Base_function2(struct Base *this) { 
    D1_function2((struct D1*) this); 
} 

// table data for D1 
static const struct table_D1 table_D1_for_D1 = {D1_function1, D1_function2}; 

// IMPORTANT table 
static const struct table_Base table_Base_for_D1 = {D1Base_function1, D1Base_function2}; 

// Constructor for derived class D1 
void D1_Ctor(struct D1 *this) 
{ 
    Base_Ctor(&this->base); // Base class vtable is initialized. 

    // Now, Override virtual function pointers 
    this->base.vtbl = &table_Base_for_D1; // Replace the vtable 
    this->mode_data = 100; 
} 

在內部,這是編譯器爲了實現虛擬功能的正確行爲而遵循的邏輯。

ptr->function1(); 
ptr->function2(); 

你可以用上面描述的vtable邏輯跟蹤上面的方法調用,看看它是否工作。

1

該類的第一個元素是usuaylly(隱藏)vtable指針 - vptr。對於多態類,vtable首先被初始化爲基類構造函數中的基類的vtable。然後,當派生類構造函數執行相同的vtable指針時,初始化指針指向派生類vtable。請注意,基類vtable指向函數1和函數2的基本版本,而派生類vtable指向函數1和函數2的派生版本。 現在,當一個指向基類指向派生類的實例,這就是「通常可以」發生:

class base 
{ 
    //int* vptr;   //hidden vtable pointer, created by compiler for polymorphic class. vptr points to base class vtable for base clas objects 
public: 
    virtual void function1(){std::cout <<"base::function1()"<<std::endl;} 
    virtual void function2(){std::cout <<"base::function2()"<<std::endl;} 
}; 

class derived: public base 
{ 
    //int* vptr;   //hidden vtable pointer, inherited from the base class. vptr points to derived class vtable for derived class objects 
public: 
    virtual void function1(){std::cout <<"derived::function1()"<<std::endl;} 
    virtual void function2(){std::cout <<"derived::function2()"<<std::endl;} 
}; 


int main() 
{ 
    typedef void (*vtableFnPtr)();  

    base* pBase; 
    base base_obj; 
    derived derived_obj; 

    pBase = &derived_obj;     //base pointer pointing to derived object 

    //one of the several possible implementations by compiler 
    int* vtableCallBack = *(int**)&derived_obj; //read the address of vtable pointed by the hidden vptr in the derived_obj 

    //pBase->function1(); 
    ((vtableFnPtr)vtableCallBack[0])();  //calls derived::function1(), when application calls pBase->function1(); 

    //pBase->function2(); 
    ((vtableFnPtr)vtableCallBack[1])();  //calls derived::function2(), when application calls pBase->function2(); 

    pBase = &base_obj; 

    //one of the several possible implementations by compiler 
    vtableCallBack = *(int**)&base_obj;  //base pointer pointing to base object 

    //pBase->function1(); 
    ((vtableFnPtr)vtableCallBack[0])();  //calls base::function1(), when application calls pBase->function1(); 

    //pBase->function2(); 
    ((vtableFnPtr)vtableCallBack[1])();  //calls base::function2(), when application calls pBase->function2(); 

} 

注意,C++編譯器不說用於對實施方法什麼實現多態行爲,因此只要行爲是多態的,就完全取決於編譯器使用vtable或任何其他實現。然而,vtable仍然是實現多態行爲最廣泛使用的方法之一。