意外输出...如何在虚拟表中进行功能绑定

时间:2018-04-05 11:53:54

标签: c++ virtual

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和派生类创建虚拟表条目。

2 个答案:

答案 0 :(得分:1)

伪代码如下所示:

#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中描述的虚拟化过程。

答案 1 :(得分:1)

构造类对象时会创建虚拟表。构造Derived对象时,它将首先调用Base构造函数(创建vtable并将其自己的Base::fun1写入其中。然后Derived构造函数运行并覆盖fun1的vtable条目及其自己的实现(Derived::fun1)。

如果你那么,在任何后来的点(甚至从任何Base函数内)调用这样一个对象实例的fun1,它将查看vtable并调用它在那里找到的任何函数。如上所述,构造后Derived::fun1对象的vtable中为Derived,因此这将被调用。您当前处于Base函数中并不重要,vtable条目不会更改。

请注意构建期间的,vtable未完全设置:如果您要从fun1构造函数中调用Base,则不< / em>致电Derived::fun1,但Base::fun1因为Derived尚未替换vtable条目。

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