意外输出..函数绑定在虚拟表中的发生方式

Unexpected Output...how function binding happens in virtual table

本文关键字:方式 虚拟 输出 函数 绑定 意外      更新时间:2023-10-16
class Base
{
public:
int a;
virtual void fun1()
{
cout<<"Hello Base"<<endl;
}
void fun2()
{
fun1();
}
};
class Derived: public Base
{
int a;
void fun1()
{
cout<<"Hello Derived"<<endl;
}
};

int main()
{
Base * B = new Derived;
B->fun2();
return 1;
}   

请帮助我理解为什么输出是 Hello Derived.这个函数绑定是如何发生的。如何为 Base 类和派生类创建虚拟表条目。

虚拟表是在构造类对象时创建的。当您构造一个Derived对象时,它将首先调用Base构造函数(该构造函数创建 vtable 并将自己的Base::fun1写入其中。然后,Derived构造函数运行并使用自己的实现 (Derived::fun1) 覆盖 vtable 条目以供fun1

。然后,如果您在以后的任何时间点(甚至从任何Base函数中)调用此类对象实例的fun1,它将查看 vtable 并调用它在那里找到的任何函数。如上所述,构造后Derived对象的 vtable 中是Derived::fun1,因此这是将被调用的。无论您当前处于Base函数中,vtable 条目都不会更改。

请注意,在构造过程中,vtable 尚未完全设置:如果要从Base构造函数中调用fun1,则不会调用Derived::fun1而是Base::fun1,因为Derived尚未替换 vtable 条目。

另请注意,完全指定函数(例如,在Derived实例上调用Base::fun1())不会执行 vtable 查找,而是完全使用指定的函数。

伪代码如下所示:

#include <iostream>
class Base{
protected:
void **vt_ptr;
public:
int a;
//Executed prior to base member initialization
//and reexecuted prior to Base destructor call
void set_dynamic_type(){
vt_ptr = Base::vtable;
}
/*virtual*/
void fun1(){
reinterpret_cast<void(*)(Base&)>(vt_ptr[1])(*this);
}
void fun2(){
fun1();
}
private:
static void func1_def(Base& pseudo_obj_arg){
Base* this=&pseudo_obj_arg;
std::cout<<"Hello Base"<<std::endl;
}
static void* vtable[2];
};
void* Base::vtable[2]={
reinterpret_cast<void*>(&type_id_of_Base),
reinterpret_cast<void*>(&Base::func1_def)};
class Derived: public Base
{
int a;
//Executed after Base intialization, 
//before Derived member initialization.
//Reexecuted prior to Derived destructor call
void set_dynamic_type(){
Base::vt_ptr = Derived::vtable;
}

private:
static void func1_def(Base& pseudo_obj_arg){
Derived* this=static_cast<Derived*>(&pseudo_obj_arg);
std::cout<<"Hello Derived"<<std::endl;
}
static void* vtable[2];  
};
void* Derived::vtable[2]={
reinterpret_cast<void*>(&type_id_of_Derived),
reinterpret_cast<void*>(&Derived::func1_def)};

也是一个限定调用,如:obj.Base::func1()直接调用Base::func1_def(obj),否则它会抛出Base::func1中描述的去虚拟化过程。