严谨声明以下结论均为GCC编译和UNIX 64位系统环境下得出的结论,非GCC和UNIX环境结论部分结论可能有所不同
1.C++类成员变量分为两部分:类自身声明的成员变量,来自于基类的成员变量
2.成员变量在内存中的顺序和声明顺序保持一致,从低地址向高地址扩展
3.直接继承的情况下, 基类成员变量的地址,在子类成员变量的地址之前;
4.虚继承的情况下,基类成员变量的地址,在子类成员变量的地址之后;
5.虚函数表指针,处于所有对象指针之前
完整的验证代码:
#include
using namespace std;
class base
{
public:
int base_a;
virtual void method(){};
};
class A: public base
{
public:
int a;
int b;
int c;
virtual void method(){};
};
int main()
{
A obj;
cout<<"address obj:"<<&obj<
有虚函数的情况下输出(64位的环境,一个指针占8个字节):
address obj:0x7ffee9fe99b0 #(对象起始地址)
address base_a:0x7ffee9fe99b8 #(基类的一个成员变量起始地址)
address a:0x7ffee9fe99bc #(子类的第一个成员变量起始地址)
address b:0x7ffee9fe99c0 #(子类的第二个成员变量起始地址)
address c:0x7ffee9fe99c4 #(子类的第三个成员变量起始地址)
可以看出(对象起始地址)和(基类的一个成员变量起始地址)中间差了一个指针的空间,把基类和子类的虚函数注释掉之后看看输出:
address obj:0x7ffeec0d39c0 #(对象起始地址)
address base_a:0x7ffeec0d39c0 #(基类的一个成员变量起始地址)
address a:0x7ffeec0d39c4
address b:0x7ffeec0d39c8
address c:0x7ffeec0d39cc
所以可以猜想虚函数指针被编译器安插在了对象的头部,指针占用固定长度字节。
虚继承的情况下程序输出:
address obj:0x7ffee15359b0 #(对象起始地址)
address base_a:0x7ffee15359c4 #(基类成员变量在最后)
address a:0x7ffee15359b8 #(子类的第一个成员变量起始地址)
address b:0x7ffee15359bc #(子类的第二个成员变量起始地址)
address c:0x7ffee15359c0 #(子类的第三个成员变量起始地址)