深入理解C++ 虚函数表
Posted mered1th
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解C++ 虚函数表相关的知识,希望对你有一定的参考价值。
深入理解C++ 虚函数表
? C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
? 这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
本文将详细介绍虚函数表的实现及其内存布局。
虚函数表
虚函数表是指在每个包含虚函数的类中都存在着一个函数地址的数组。当我们用父类的指针来操作一个子类的时候,这张虚函数表指明了实际所应该调用的函数。
C++的编译器保证虚函数表的指针存在于对象实例中最前面的位置,这样通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
按照上面的说法,来看一个实际的例子:
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "f()" << endl; }
virtual void g() { cout << "g()" << endl; }
virtual void h() { cout << "h()" << endl; }
};
int main()
{
Base t;
( ((void(*)())*((int*)(*((int*)&t)) + 0)) ) ();
( ((void(*)())*((int*)(*((int*)&t)) + 1)) ) ();
( ((void(*)())*((int*)(*((int*)&t)) + 2)) ) ();
return 0;
}
经过VS2017,x86测试:
我们成功地通过实例对象的地址,得到了对象所有的类函数。
main定义Base类对象t,把&b转成int *
,取得虚函数表的地址vtptr就是:(int*)(&t)
,然后再解引用并强转成int *
得到第一个虚函数的地址,也就是Base::f()即(int*)(*((int*)&t))
,那么,第二个虚函数g()的地址就是(int*)(*((int*)&t)) + 1
,依次类推。
单继承下的虚函数表
派生类未覆盖基类虚函数
下面我们来看下派生类没有覆盖基类虚函数的情况,其中Base类延用上一节的定义。从图中可看出虚函数表中依照声明顺序先放基类的虚函数地址,再放派生类的虚函数地址。
可以看到下面几点:
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
派生类覆盖基类虚函数
多继承下的虚函数表
无虚函数覆盖
有虚函数覆盖
菱形继承
发现有很多优秀的大神写的更加详尽,具体可以参考以下链接:
https://coolshell.cn/articles/12165.html
https://jocent.me/2017/08/07/virtual-table.html
https://blog.csdn.net/lihao21/article/details/50688337
以上是关于深入理解C++ 虚函数表的主要内容,如果未能解决你的问题,请参考以下文章