1、C++对象在内存中的布局
在vs2013+win10环境下,数据的对齐为8个字节。
当某个基类有虚函数时,这个类生成一个表格,称为虚表(virtual table,简称vtbl)。虚表中存放着一堆指针,这些指针指向该类每一个虚函数。虚表中的函数地址将按声明时的顺序排列。每个类对象都拥有一个虚表指针(vptr),由编译器为其生成。虚表指针的设定与重置皆由类的复制控制(也即是构造函数、析构函数、赋值操作符)来完成。vptr的位置为编译器决定,传统上它被放在所有显示声明的成员之后,不过现在许多编译器把vptr放在一个类对象的最前端。另外,虚函数表的前面设置了一个指向type_info的指针,用以支持RTTI(Run Time Type Identification,运行时类型识别)。RTTI是为多态而生成的信息,包括对象继承关系,对象本身的描述等,只有具有虚函数的对象在会生成。对象的数据则接着vprt,对齐方式为8个字节。
当该类为子类时,对象的首地址存放vptr,若子类重写(overwrite)了父类的虚函数,则子类虚函数将覆盖父类虚表中对应的函数;若子类并无overwrite父类虚函数,而是声明了自己新的虚函数,则该虚函数地址将扩充到父类虚函数表最后(在vs中无法通过监视看到扩充的结果,不过我们通过取地址的方法可以做到,子类新的虚函数确实在父类子物体的虚函数表末端)。接着是基类的数据,最后是子类的自己定义的数据。
取址的方式如下:
cout << "虚函数表第一个函数的地址:" << (int *)*((int*)(&p)) << endl;
cout << "析构函数的地址:" << (int* )*(int *)*((int*)(&p)) << endl;
cout << "虚函数表中,第二个虚函数即print()的地址:" << ((int*)*(int*)(&p) + 1) << endl;