by CSDN hairetz
第一部分:
先看一个空的类占多少空间?
- class Base
- {
- public:
- Base();
- ~Base();
- };
注意到我这里显示声明了构造跟析构,但是sizeof(Base)的结果是1.
因为一个空类也要实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。
而析构函数,跟构造函数这些成员函数,是跟sizeof无关的,也不难理解因为我们的sizeof是针对实例,而普通成员函数,是针对类体的,一个类的成员函数,多个实例也共用相同的函数指针,所以自然不能归为实例的大小,这在我的另一篇博文有提到。
接着看下面一段代码
- class Base
- {
- public:
- Base();
- virtual ~Base(); //每个实例都有虚函数表
- void set_num(int num) //普通成员函数,为各实例公有,不归入sizeof统计
- {
- a=num;
- }
- private:
- int a; //占4字节
- char *p; //4字节指针
- };
- class Derive:public Base
- {
- public:
- Derive():Base(){};
- ~Derive(){};
- private:
- static int st; //非实例独占
- int d; //占4字节
- char *p; //4字节指针
- };
- int main()
- {
- cout<<sizeof(Base)<<endl;
- cout<<sizeof(Derive)<<endl;
- return 0;
- }
结果自然是
12
20
Base类里的int a;char *p;占8个字节。
而虚析构函数virtual ~Base();的指针占4子字节。
其他成员函数不归入sizeof统计。
Derive类首先要具有Base类的部分,也就是占12字节。
int d;char *p;占8字节
static int st;不归入sizeof统计
所以一共是20字节。
在考虑在Derive里加一个成员char c;
- class Derive:public Base
- {
- public:
- Derive():Base(){};
- ~Derive(){};
- private:
- static int st;
- int d;
- char *p;
- char c;
- };
这个时候,结果就变成了
12
24
一个char c;增加了4字节,说明类的大小也遵守类似class字节对齐,的补齐规则。
具体的可以看我那篇《5分钟搞定字节对齐》
至此,我们可以归纳以下几个原则:
1.类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑。
2.普通成员函数与sizeof无关。
3.虚函数由于要维护在虚函数表,所以要占据一个指针大小,也就是4字节。
4.类的总大小也遵守类似class字节对齐的,调整规则。
第二部分:
上一篇文章研究了关于类大小的4条规则后,我们再结合虚函数表,来研究下类的大小。
- class Base
- {
- public:
- Base(){};
- virtual ~Base(){};
- void set_num(int num)
- {
- a=num;
- }
- virtual int get_num()
- {
- return a;
- }
- private:
- int a;
- char *p;
- };
- class Derive:public Base
- {
- public:
- Derive():Base(){};
- ~Derive(){};
- virtual int get_num()
- {
- return d;
- }
- private:
- static int st;
- int d;
- char *p;
- char c;
- };
- int main()
- {
- cout<<sizeof(Base)<<endl;
- cout<<sizeof(Derive)<<endl;
- return 0;
- }
在Base类里添加了virtual int get_num()函数,而子类也重新实现了virtual int get_num()函数。
但是结果依然是
12
24
说明子类只是共用父类的虚函数表,因此一旦父类里有虚函数,子类的虚函数将不计入sizeof大小。
这可以认为是一个补充规则。
第三部分:
看这段代码
- class Top
- {
- protected:
- int x;
- public:
- Top(int n):x(n){cout<<"Top"<<endl;}
- virtual ~Top(){}
- };
- class Left:virtual public Top
- {
- protected:
- int y;
- public:
- Left(int m,int n):Top(m){y=n;cout<<"Left"<<endl;}
- };
- class Right:public virtual Top
- {
- protected:
- int z;
- public:
- Right(int m,int n):Top(m){z=n;cout<<"Right"<<endl;}
- };
- class Bottom:public Left,public Right
- {
- int w;
- public:
- Bottom(int i,int j,int k,int m):Top(i),Left(i,j),Right(i,k),w(m)
- {
- cout<<"Bottom"<<endl;
- }
- };
- int main()
- {
- Bottom b(1,2,3,4);
- cout<<"sizeof(b) "<<sizeof(b)<<","<<sizeof(Bottom)<<endl;
- cout<<sizeof(Left)<<","<<sizeof(Right)<<","<<sizeof(Top)<<endl;
- for(int i=0;i<sizeof(b);i+=4)
- cout<<*(reinterpret_cast<int*>((&b)+i))<<"________"<<endl;
- system("PAUSE");
- }
结果如下:
Top
Left
Right
Bottom
sizeof(b) 28,28
16,16,8
4657244________
2367460________
0________
746________
44________
5701724________
4________
请按任意键继续. . .
也就是说Top为占8字节,这好理解。
int x; //4字节
virtual ~Top(){} //基类的虚表入口,4字节
接着看Left跟Right都是16字节。
本来除了Top的8字节,Left里只有int y; 占4字节,还有4字节占在那里?
由于是虚继承,虚继承的子类都要包含一个指向基类的指针,从而实现动态联编。
一次,要额外加4字节的空间。所以一共是8+4+4=16字节。
Right同理。
再看Bottom的大小为28字节,这个是怎么算的呢?
虚继承是棱形继承,基类大小为8字节.
而Bottom为普通多继承,因此,Bottom的大小应该是Bottom部分+Left部分+Right部分+各自指向基类的指针+基类大小(虚继承导致只有一个基类实例)。
Top
/ /
/ /
Left Right
/ /
/ /
Bottom
现在算算,基类8字节+Left4字节+Right4字节+4字节指向基类的指针*2+Bottom4字节=28字节。
关于函数以及变量是否统计入sizeof实例,请参考我前两篇。
纯粹自己根据资料推导,欢迎指正。