2016-03-08 2 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

Компилятор в какой-то момент решает и принимает во внимание свои внутренние структуры данных, как и для всего остального. Обычно алгоритм должен зависеть только от определения класса, так что в каждом ТУ ему ничего не нужно, чтобы создать тот же виртуальный стол, что и в каждом другом ТУ. –

+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

Первый элемент класса является обычным (скрытым) указателем vtable - vptr. Для полиморфного класса vtable сначала инициализируется vtable базового класса в конструкторе базового класса. Затем, когда конструктор производного класса выполняет тот же самый указатель vtable, инициализируется, чтобы указать на производный класс vtable. Обратите внимание, что базовый класс vtable указывает на базовую версию функции1 и function2, тогда как производный класс vtable указывает на производную версию функции1 и function2. Теперь, когда указатель точек базового класса к экземпляру производного класса, это то, что «как правило, может» случиться:

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 остается одним из наиболее широко используемых методов для достижения полиморфного поведения.