如何在 c++ 中决定虚拟表中的索引

How indexing within the virtual table is decided in c++?

本文关键字:索引 虚拟 决定 c++      更新时间:2023-10-16

请考虑以下代码。

  #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]?

类的第一个元素通常是(隐藏的)vtable指针 - vptr。对于多态类,vtable 首先初始化为基类构造函数中基类的 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 仍然是实现多态行为的最广泛使用的方法之一。

"计算机科学中的所有问题都可以通过另一个层次的间接解决"

我提到了优秀而详细的博客文章,并在下面尝试为您的简单示例解释 vtable 的使用。看看你是否读透了。

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 逻辑跟踪上面的方法调用,看看它是否有效。