带有虚表的类的内存分布总结
这个问题一直似是而非的,昨天闲着无事,便细看一下,发现还是挺容易的。
总结起来可以分为以下几块:
1、空类的内存分布
2、带变量的类的内存分布
3、带虚函数的类的内存分布
4、带虚函数的子类的内存分布
5、关于虚析构的描述
6、关于纯虚函数为何必须在子类中实现的问题。
未完成部分:
1、关于虚基类的结构分布。
1、空类的内存分布比较简单,一般用一个字节表示,据说是为了标识类而作的特别的安排。如下代码:
class A{}
则sizeof(A) 为1.
2、如果类中包含变量,则类的大小为变量的大小。
3、类中一旦带有虚函数,则类的大小增加4个字节,前4个字节(针对32位机器)为虚表的入口地址,此地址指向一个数组,用来存放虚函数的地址
4、子类中虚表指向的数组,对于未覆盖的虚函数,直接沿用父类的虚函数地址,已经覆盖的则改写成子类的虚函数地址
5、虚析构函数也是虚函数,在虚表数组的最后一个,由于虚析构调用时由操作系统加入某些参数,因此不能手工调用。
6、我们知道,纯虚函数的接口在子类中必须全部实现,否则程序会出错,原因是纯虚函数在父类中由于没有实现,系统指向的是一个错误地址,子类若有部分未实现的话,会依样把那些地址也放入虚表中,造成错误。
最后以一个简单的例子来结束,代码可以打印出各变量及虚表的地址:
#include "stdafx.h" #include < iostream > using namespace std; class Base { public: Base() { cout << "In Base" << endl; cout << "Virtual Pointer = " << (int*)this << endl; cout << "Address of Vtable = " << (int*)*(int*)this << endl; cout << "Value at Vtable 1st entry = " << (int*)*((int*)*(int*)this+0) << endl; cout << "Value at Vtable 2nd entry = " << (int*)*((int*)*(int*)this+1) << endl; cout << endl; } int base_; virtual void f1() = 0; virtual void f2() = 0; }; class MostDrive : public Base { public: MostDrive() { cout << "In MostDrive" << endl; cout << "Virtual Pointer = " << (int*)this << endl; cout << "Address of Vtable = " << (int*)*(int*)this << endl; cout << "Value at Vtable 1st entry = " << (int*)*((int*)*(int*)this+0) << endl; cout << "Value at Vtable 2nd entry = " << (int*)*((int*)*(int*)this+1) << endl; cout << endl; } void test() { cout << "------------------------test---------------------"<<endl; cout << "In Drive" << endl; cout << "Virtual Pointer = " << (int*)this << endl; cout << "Address of Vtable = " << (int*)*(int*)this << endl; cout << "Value at Vtable 1st entry = " << (int*)*((int*)*(int*)this+0) << endl; cout << "Value at Vtable 2nd entry = " << (int*)*((int*)*(int*)this+1) << endl; cout << endl; } virtual void f1() { cout << "MostDrive::f1" << endl; } virtual void f2() { cout << "MostDrive::f2" << endl; } int mostdrive_; }; typedef void(*Fun)(); int main() { MostDrive d; d.base_ = 1; d.mostdrive_ = 3; d.test(); cout << "------------in main()-----------------------"; cout << "sizeof(MostDrive)=" <<sizeof(MostDrive) << endl; cout << "Virtual Pointer = " << (int*)&d << endl; cout << "Address of Vtable = " << (int*)*(int*)&d << endl; cout << "Value at Vtable 1st address = " << ((int*)*(int*)&d+0) << endl; cout << "Value at Vtable 1st entry = " << (int*)*((int*)*(int*)&d+0) << endl; cout << "Value at Vtable 2nd address = " << ((int*)*(int*)&d+1) << endl; cout << "Value at Vtable 2nd entry = " << (int*)*((int*)*(int*)&d+1) << endl; Fun pFun1 = (Fun)*((int*)*(int*)&d+0); pFun1(); Fun pFun2 = (Fun)*((int*)*(int*)&d+1); pFun2(); cout << "first value address = " <<(int*)(int*)&d + 1 <<endl; cout << "first value = " << *((int*)&d + 1) << endl; cout << "second value address = " <<(int*)(int*)&d + 2 <<endl; cout << "second value = " << *((int*)&d + 2) << endl; return 0; }
运行结果:
In Base
Virtual Pointer = 0012FF58
Address of Vtable = 004184D8
Value at Vtable 1st entry = 004111DB
Value at Vtable 2nd entry = 004111DB
In MostDrive
Virtual Pointer = 0012FF58
Address of Vtable = 00417940
Value at Vtable 1st entry = 00411145
Value at Vtable 2nd entry = 004110C8
------------------------test---------------------
In Drive
Virtual Pointer = 0012FF58
Address of Vtable = 00417940
Value at Vtable 1st entry = 00411145
Value at Vtable 2nd entry = 004110C8
------------in main()-----------------------sizeof(MostDrive)=12
Virtual Pointer = 0012FF58
Address of Vtable = 00417940
Value at Vtable 1st address = 00417940
Value at Vtable 1st entry = 00411145
Value at Vtable 2nd address = 00417944
Value at Vtable 2nd entry = 004110C8
MostDrive::f1
MostDrive::f2
first value address = 0012FF5C
first value = 1
second value address = 0012FF60
second value = 3
图示如下: