刚开始学习C++虚函数的时候,只知道虚函数就是用来构成动态多态的,以及如何使用虚函数。但是我发现当碰到稍微复杂的多态问题的时候,就很难以理解其实现原理,导致使用出错,比如:一个派生类继承基类,生成派生类对象有两种方式:调用基类指针生成对象、调用派生类指针生成对象,调用完毕后释放对象,哪种方式必须要用虚函数实现基类和派生类的析构函数,如果不用会出现什么问题?(可移步基类的析构函数是否要定义为虚函数)。而《C++ Primer》中更多的是对虚函数使用的讲解,查阅有关虚函数实现多态的博客也大都千篇一律,这对学习者从底层理解虚函数实现多态的原理帮助甚微。本文将从内存角度上剖析一下虚函数实现多态的底层原理,希望能给C++学习者提供帮助。
一、无继承时,含虚函数类的内存布局剖析
无继承的类中定义虚函数实际上没有意义的,因为虚函数存在的意义就是为了实现动态多态,即将虚函数看作一个接口去调用基类和派生类中同一个函数实现不同的功能。但是为了更好地展示从无继承到有继承时虚函数引起的内存布局变化情况,这里首先定义一个无继承类进行分析。
class Base
{
public:
virtual void fun1() {
};
virtual void fun2() {
};
virtual void fun3() {
};
void fun4() {
};
private:
int m_A;
int m_B;
};
在vs2013中完成类定义后,选择“项目—>属性—>C/C++命令行—>其他选项”,在这里写上/d1 reportSingleClassLayoutBase,然后生成解决方案,输出行就会显示Base类的内存布局,如下图所示:
由图中可知,Base类的内存大小为12字节,首先存放的是虚表指针vfptr(占有4个字节),指向虚函数表vftable的首地址;接着按声明顺序依次存放类的变量m_A、m_B(各占有4个字节)。其中,虚函数表vftable按声明顺序依次存放类中定义的3个虚函数fun1、fun2、fun3的地址。我们发现,Base类的内存布局中并没有函数fun4,这是因为fun4是一个普通的成员函数,在类定义的时候就会存储在代码区,所以不在Base类的内存布局中。
注意:在设置“项目—>属性—>C/C++命令行—>其他选项”时,若要查看项目中所有类的内存布局,就写上"/d1 reportAllClassLayout";若查看类XXX的内存布局时,就写上"/d1 reportSingleClassLayoutXXX"。
二、单一继承时,含虚函数类的内存布局剖析
定义一个Child类继承Base类,类的定