c++虚函数与虚表深度解析

来源:互联网 发布:开淘宝的心得体会 编辑:程序博客网 时间:2024/05/20 00:13

一.虚函数是怎么被找到的

首先来看一下单继承,编译下面的代码:

#include<iostream>#include<string.h>using namespace std;class Grandam{public:    virtual void introduce_self()    {        cout << "I am grandam." << endl;    }};class Mother:public Grandam{public:    virtual void introduce_self()    {        cout << "I am mother." << endl;    }};class Daughter :public Mother{public:    void introduce_self()    {        cout << "I am daughter." << endl;    }};int main(){    Grandam* ptr;    Grandam g;    Mother m;    Mother m1;    Daughter d;    typedef void(*introduce_self)();     ptr = &g;    ptr->introduce_self();    ptr = &m;    ptr->introduce_self();        ptr = &m1;    ptr = &d;    ptr->introduce_self();    introduce_self* p = (introduce_self*)(*(int*)(&g));      (*p)();    system("pause");    return 0;}

结果如下图所示:

这里写图片描述

第四个结果就是调用的是基类Grandam中的虚函数。

代码中:

typedef void(*introduce_self)();   //对函数进行重命名introduce_self* p = (introduce_self*)(*(int*)(&g));  //定义一个函数指针,并让它指向对象内存的首地址里存放的地址(即虚表指针或地址)(*p)();   //对虚表调用该函数因为虚表里存放的是虚函数的地址,所以对函数指针进行解引用,即就是调用该虚函数。

从而我们得知:

只要存在虚函数,那么被基类指针或被基类对象引用的对象中调用该虚函数时,该被调用对象的首地址存放的是虚表指针_vfptr(地址),而虚表_vfptr中存放的是虚函数地址。

二.基类、派生类是否共用一个虚表

我们也可以按f10对它进行单步调试,看内存中存放内容的变化, 如图所示:

1. 对基类Grandam对象g的函数introduce_self()调用时的内存如下图所示:

这里写图片描述

2. 对派生类Mother对象m的函数introduce_self()调用时的内存如下图所示:

这里写图片描述

3. 对派生类Mother对象m的函数introduce_self()调用时的内存如下图所示:

这里写图片描述

4. 对派生类Daughter对象d的函数introduce_self()调用时的内存如下图所示:

这里写图片描述

总结:根据三个内存中的对比我们知道,不同对象地址中存放的虚表指针不同,而同类对象共用一个虚表。

三.基类、派生类调用虚函数的顺序

1. 单继承,代码如下:

#include<iostream>#include<string.h>using namespace std;class Grandam{public:    void introduce_self1()    {        cout << "1: I am grandam." << endl;    }    void introduce_self2()    {        cout << "2: I am grandam." << endl;    }    virtual void introduce_self3()    {        cout << "3: I am grandam." << endl;    }    virtual void introduce_self4()    {        cout << "4: I am grandam." << endl;    }};class Daughter :public Grandam{public:    void introduce_self2()    {        cout << "2: I am daughter." << endl;    }    virtual void introduce_self5()    {        cout << "5: I am daughter." << endl;    }    virtual void introduce_self1()    {        cout << "1: I am daughter." << endl;    }    virtual void introduce_self4()    {        cout << "4: I am daughter." << endl;    }};int main(){    Grandam g;    Daughter d;    Daughter* ptr = &d;    typedef void(*intro)();    intro* pi = (intro*)(*((int*)(&d)));    while (*pi)    {        (*pi)();        (int *)pi++;    }    return 0;}

**下面这段代码,是对虚表中虚函数的调用,注意事项如下分析:

typedef void(*intro)();    intro* pi = (intro*)(*((int*)(&d)));    while (*pi)    {        (*pi)();        (int *)pi++;    }

注意: 是对派生类对象d的虚表的调用,这是在vs 2012下运行的,因为虚表的最后一个地址为00000000,所以可写成此while循环,不同的编译器,有不同的写法。

运行结果如下图:

这里写图片描述

总结我们可以看出单继承下
按照对象地址中存放的虚表指针 :虚表里面存放虚函数的地址顺序, 去调用虚函数:

1).首先调用基类中的虚函数,按在基类中声明的顺序调用。

2).基类的虚函数调用完后,再调用派生类中自带的虚函数,放在基类函数的后面。

3).调用派生类的虚函数时,也是按照在派生类中声明顺序调用的。

2.多继承如下代码:

#include<iostream>#include<string.h>using namespace std;class Grandam{public:    void introduce_self1()    {        cout << "1: I am grandam." << endl;    }    void introduce_self2()    {        cout << "2: I am grandam." << endl;    }    virtual void introduce_self3()    {        cout << "3: I am grandam." << endl;    }    virtual void introduce_self4()    {        cout << "4: I am grandam." << endl;    }};class Mother {public:    virtual void introduce_self1()    {        cout << "1: I am mother." << endl;    }    virtual void introduce_self2()    {        cout << "2: I am mother." << endl;    }};class Daughter :public Grandam , public Mother{public:    void introduce_self2()    {        cout << "2: I am daughter." << endl;    }    virtual void introduce_self5()    {        cout << "5: I am daughter." << endl;    }    void introduce_self1()    {        cout << "1: I am daughter." << endl;    }    virtual void introduce_self4()    {        cout << "4: I am daughter." << endl;    }};int main(){    Grandam g;    Mother m;    Daughter d;    Mother& m1 = d;    Grandam& g1 = d;    typedef void(*pfun)();    pfun* pg1 = (pfun*)(*(int*)(&g1));    while (*pg1)    {        (*pg1)();        (int*)pg1++;    }    pfun* pm1 = (pfun*)(*(int*)(&m1));    while (*pm1)    {        (*pm1)();        (int*)pm1++;    }    return 0;}

结果如下图:

这里写图片描述

总结多继承情况下,派生类自有的虚函数 在调用完第一个基类的虚函数 后面调用。

0 0