C++原理剖析之虚函数表

最近在看C++的一些相关的机制,再加上刚看了陈皓大神的早期关于虚函数表的博客,便自己动手通过编程了解了下虚函数表的原理。

前言

c++是通过虚函数来实现多态的的机制。我们可以通过将父类的指针指向子类的实例,如Base b = new Derive(),如此一来,如果子类Derive中重载了父类中的一个函数h(),那么调用b->h()等同于调用Derive dd->h()。我们这里来剖析下虚函数表的实现机制。

1 虚函数表的内部结构

我们可以通过编程来对虚函数表的内部结构来一窥究竟。如下代码:

#include <iostream>
using namespace std;
class Base{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
};

我们构造了一个基类,定义了三个虚函数f(),g(),h(),分别输出对应的描述。我们通过以下代码来得到该基类的虚函数表地址,和虚函数表中各个函数的地址。

int main() {
    Base1 b;
    cout << "虚函数表地址:" << (long*)(&b)+1 << endl;
    cout << "虚函数表-第一个函数地址:" << ((long*)*(long*)(&b)) << endl;
    cout << "虚函数表-第二个函数地址:" << ((long*)*(long*)(&b)+1) << endl;
    cout << "虚函数表-第三个函数地址:" << ((long*)*(long*)(&b)+2) << endl;
}

我的机器是64位系统,其中函数指针大小为16位,因此使用long*强转。运行结果如下:

虚函数表地址:0x7fffb023d7a8
虚函数表-第一个函数地址:0x466c88
虚函数表-第二个函数地址:0x466c90
虚函数表-第三个函数地址:0x466c98

为了进一步验证这些函数地址的有效性,使用一个函数指针pFun来指向这些函数:

int main() {
    typedef void(*Fun)(void);
    Base1 b;
    Fun pFun = NULL;
    pFun = (Fun)*((long*)*(long*)(&b));
    pFun();
    pFun = (Fun)*((long*)*(long*)(&b)+1);
    pFun();
    pFun = (Fun)*((long*)*(long*)(&b)+2);
    pFun();
}

结果如下:

Base::f
Base::g
Base::h

由此我们可知该基类的虚函数表的结构应该是这样:
在这里插入图片描述

2 一般继承无函数重写的情况

我们来分析单继承有子类的情况,而且该子类有自己的虚函数,并无对父类的函数进行重写。数据结构如下:

class Base{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
};
class Derive{
public:
    virtual void f1() { cout << "Derive::f1" << endl; }
    virtual void g1() { cout << "Derive::g1" << endl; }
    virtual void h1() { cout << "Derive::h1" << endl; }
};

通过编程验证可知,其父类的虚函数表没有变化,其子类的虚函数表是这样:
在这里插入图片描述

3 一般继承有函数重写的情况

如果其子类有函数重写的情况,如下数据结构:

class Base{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
};
class Derive{
public:
    void f() { cout << "Derive::f" << endl; }
    virtual void g1() { cout << "Derive::g1" << endl; }
    virtual void h1() { cout << "Derive::h1" << endl; }
};

可以看出子类重写了父类的 f 函数,这时候再看子类的虚函数表结构:
在这里插入图片描述可以看出子类的f函数覆盖了父类的f函数的位置,其他位置不变。
不妨编程验证一下:

int main() {
    typedef void(*Fun)(void);
    Base b;
    Derive d;
    Fun pFun = NULL;
    pFun = (Fun)*(long*)*((long*)(&d));
    pFun();
}

结果:

Derive::f

4 多重继承无函数重写的情况


待续,,,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章