之前一节成为高手必须弄懂的问题,C++语言中类在内存中是如何存储的?讨论了C++语言中类在内存中分布模型,提到了C++语言编译器会自动为每一个拥有虚函数的类创建虚表和虚表指针,其中虚表指针指向虚表,而虚表则用于存放虚函数指针,如下图所示:
C++虚函数的内存分布基类的内存模型
那么,若是拥有虚函数的类A作为基类,派生出其他类,这些派生类如何处理类A的虚表呢?换句话说,C++语言中的派生类是如何继承基类的虚函数的呢?为了便于讨论,先将类A的C++语言代码补充完整,请看:
类A的C++语言代码在上述C++语言代码中,类A拥有两个常规函数,两个int型变量,此外它还有两个虚函数vfoo1()和vfoo2(),因此编译器处理类A时,会自动为它分配一个虚表,用于存放vfoo1()和vfoo2()两个函数的地址。我们先定义一个A对象,并将相关成员的地址打印出来,相关的C++语言代码如下,请看:
Aa;
print_addr(A,a);
因为稍后还要分析基类A的派生类成员地址,所以为了方便,将打印地址的功能定义为
print_addr()宏了,它的C++语言代码如下,请看:
print_addr()宏的代码编译并执行,得到如下输出:
输出在C++语言类的内存分布一节我们曾提到类的成员函数是独立存储的,只有成员变量和虚表指针(如果该类有虚函数的话)才会为类占据内存空间,因此对象a的size为24,正是它的3个int型的成员变量与一个虚表指针占用的内存大小。
在我的机器上,int型变量占用内存为4字节,指针占用内存大小为8字节。到这里可能有读者迷惑了,3个int型成员变量占用的内存为12字节,加上虚表指针的8字节,也才20字节,而sizeof(a)等于24,多出的4字节从哪里来呢?请参考我之前关于内存对齐的文章。
内存对齐后,对象a的内存分布如下图所示:
对象a的内存分布
派生类的内存模型
类A作为基类,肯定可以被其他派生类继承的,下面是一段C++语言代码示例:
C++语言代码示例上述C++语言代码定义了继承基类A的派生类B,接着又以类B为基类定义了派生类C。其中类B重写了由基类A继承而来的虚函数vfoo1(),类C重写了由基类B继承而来的虚函数vfoo2()。为了弄清派生类的内存模型,我们使用
print_addr()
宏将类A、B、C相关的地址打印出来:
Aa;
Bb;
Cc;
coutendl;
print_addr(A,a);
coutendl;
print_addr(B,b);
coutendl;
print_addr(C,c);
coutendl;
编译并执行这段C++语言代码,得到的输出如下:
输出先