面(笔)试的时候经常碰到求C++类的占用空间的问题,尤其是包含了虚函数的单一继承和多重继承时类的占用空间,这里作一下简单的测试和说明。
一、空类的占用空间
空类的占用空间为1个字节,《深度探索C++对象模型》是这样说的:“那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址”,也就是说空类的这个char是用来标识类的不同对象的。这个很容易验证,需要注意的是,这个char是不会进行字节对齐的。
二、单一继承时类的占用空间
#include <iostream>
using namespace std;
class A
{};
class B : public A
{
public:
void func1(){}
};
class C : public B
{
public:
virtual ~C(){}
virtual void func2(){}
};
class D : public C
{
public:
virtual ~D(){}
virtual void func1(){}
virtual void func3(){}
char c;
};
class E : public D
{
public:
double x;
static int y;
};
int main()
{
cout << "A: " << sizeof(A) << endl;
cout << "B: " << sizeof(B) << endl;
cout << "C: " << sizeof(C) << endl;
cout << "D: " << sizeof(D) << endl;
cout << "E: " << sizeof(E) << endl;
return 0;
}
输出结果:
A: 1
B: 1
C: 8
D: 16
E: 24
说明:
(1)由A知,空类的大小是1字节
(2)由B知,类的占用空间与成员函数无关
(3)由C知,存在虚函数时,类的占用空间会变大,这里实际上是插入了一个虚表指针(vptr),这个指针指向类的虚函数列表。指针的大小与机器的位数有关,32位机器上指针大小为4个字节,64位机器上是8个字节。
(4)由D知,类的占用空间与成员变量有关。D的大小是类C的占用空间加上char成员变量的1个字节,这里存在字节对齐,即把1一个字节补齐到8个字节,这里浪费掉了7个字节的空间。
(5)由E知,类的占用空间与虚函数的多少无关,都是只有一个虚表指针的大小;与静态成员变量无关,静态成员变量存在于内存的全局存储区,在编译时,编译器将其剔除类的空间,相当于一个全局变量。E的大小是类D的大小加上double变量的大小,是24字节。
(6)假如类E在double成员变量之前有个char成员变量,再有一个int成员变量,那么类E的大小是多少呢?新增加的两个成员变量会不会继续占用类D空出来的7个字节呢?假如这两个变量在double成员变量之后呢?再假如char变量和int变量换一下位置呢?像这样:
class E1 : public D
{
public:
char a;
int m;
double x;
static int y;
};
class E2 : public D
{
public:
int m;
char a;
double x;
static int y;
};
class E3 : public D
{
public:
double x;
char a;
int m;
static int y;
};
class E4 : public D
{
public:
double x;
int m;
char a;
static int y;
};
类E1、E2、E3、E4的大小分别是多少呢?在我的机器上(64位),分别是:
E1: 24
E2: 32
E3: 32
E4: 32
类E1的大小居然等于E的大小,24字节,这样看的话E1的char和int成员变量的确可能占用了类D的剩余的7个字节中的5个。但是,类E2的大小却又是32字节,也就是说类E2的int和char成员变量是没有占用类D的剩余字节的。E1和E2的区别只在于前两个成员变量的声明顺序,E2却多出了8个字节,这是什么原因呢?求解。
E3和E4与预想的一样,都是32字节。
三、多重继承时类的占用空间
class G
{
public:
void func2(){}
};
class H
{
public:
virtual ~H(){}
virtual void func4(){}
char h;
};
class G1 : public D, G
{
};
class G2 : public D, H
{
public:
char g2;
};
已经知道,G大小是1字节,H大小是16字节(存在字节对齐),G1、G2是多少呢?
按照我们的理解,G1的大小是D的大小加上G的大小,16+1=17,再字节对齐,应该是24字节;G2的大小应该是16+16+1=33,再字节对齐,应该是40字节。
但是,实际上我的运行结果却是:G1是16字节,G2是32字节。
这个结果,是不是可以说空类在继承时不计算其占用空间?G2的char成员变量是不是占用了D或者H的一个剩余空间呢?
按照《深度探索C++对象模型》中关于对象模型的说明,子类在继承父类时,保持父类的对象模型不变,并在父类对象的后面加上自己的成员变量(但会共用一个vptr),也就是说子类不应该占用父类的模型空间。不知道我的理解是否正确,各位看官,有知道以上问题解答的,烦请告知,多谢!
四、参考资料
1、http://blog.csdn.net/hitblue/article/details/3726754
2、http://www.cnblogs.com/snben/archive/2012/08/16/2641667.html