C++迟后联编和虚函数表

先看一个题目:

class Base
{
public:
    virtual void Show(int x)
    {
        cout << "In Base class, int x = " << x << endl;
    }
};

class Derived : public Base
{
public:
    virtual void Show(float x)
    {
        cout << "In Derived, float x = " << x << endl;
    }
};

void test (Base &b)
{
    int i = 1;
    b.Show(i);
    
    float f = 2.0;
    b.Show(f);
}

int main(int argc, char *argv[])
{
    Base bc;

    Derived sc;
    test(bc);
    test(sc);
 
    return 0;
}

输出结果为:D

A、In Base class, int x = 1;
   In Base class, int x = 2;
   In Derived, int x = 1;
   In Derived, float x = 2;
B、In Base class, int x = 1;
   In Base class, int x = 2;
   In Derived, float x = 1;
   In Derived, float x = 2;
C、In Base class, int x = 1;
   In Base class, int x = 2;
   In Base, int x = 1;
   In Base, float x = 2;
D、In Base class, int x = 1;
   In Base class, int x = 2;
   In Base class, int x = 1;
   In Base class, int x = 2;

理由:如果虚函数在基类与子类中出现的仅仅是名字的相同,而参数类型不同,或者返回类型不同,即使写上了virtual关键字,也不进行迟后联编。

stackoverflow上,可以看到解释,http://stackoverflow.com/questions/27227189/override-virtual-function-with-different-parameters-in-c

C++里有两种编译类型:
1) 先期联编或静态联编:在编译时就能进行函数联编称为先期联编或静态联编。
2) 迟后联编或动态联编:在运行时才能进行的联编称为迟后联编或动态联编。

virtual关键字的作用就是提示编译器进行迟后联编,告诉链接过程:“我是个虚的,先不要连接我,等运行时再说”。 具体原理:当编译器遇到virtual后,会为所在的类构造一个表和一个指针,那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址,指针叫做vptr,指向那个表。而这个指针保存在相应的对象当中,也就是说只有创建了对象以后才能找到相应虚函数的地址。 

对于下面这种常见代码(假如Base是Derive的父类):

Base *p=new Derive();
p->virtual_fun();

在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向所属类的虚表。上述程序中,由于p实际指向的对象类型是Derive,因此vptr指向的Derive类的vtable,当调用p->virtual_fun()时,根据虚表中的函数地址找到的就是Derive类的virtual_func()函数。

 

假设我们有这样的一个类:

class Base {

     public:

            virtual void f() { cout << "Base::f" << endl; }

            virtual void g() { cout << "Base::g" << endl; }

            virtual void h() { cout << "Base::h" << endl; }

};

对应的虚函数表:

假设有如下所示的一个继承关系:

对于实例:Derive d; 的虚函数表如下:

如果是多继承:

对于实例:Derive d; 的虚函数表如下:

而C++标准规定:为确保运行时的多态定义的基类与派生类的虚函数不仅函数名要相同,其返回值及参数都必须相同,否则即使加上了virtual,系统也不进行迟后联编。

因此,对于最初的题目,Base的虚函数表里仅仅有一个Show方法,由于Derived子类重载了Show函数,那么Derived的虚函数表里实际上有两个Show方法,

因此,从Base角度去调用Show方法也只能是调用Base自己的方法了。

 

虚函数表的例子参考了:http://blog.csdn.net/haoel/article/details/1948051

posted @ 2016-08-31 09:00  bonelee  阅读(1229)  评论(0编辑  收藏  举报