sizeof浅析——求类的大小

来源:互联网 发布:淘宝店铺报价单 编辑:程序博客网 时间:2024/05/20 09:45

     sizeof求类的大小和求结构体的大小,有一定的相似性,但又不完全相同,因为类存在这继承和派生、存在着虚函数。

1、空类大小

对于一个空类,使用sizeof求得其大小为1。

2、简单类

如下定义一个类,

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. private:  
  6.     char b;  
  7. };  

        使用sizeof求这种简单类,结果和求结构体的sizeof是一样的,需要考虑偏移和对齐。要注意的是static变量不属于类的一部分,如果类中定义了static变量,求sizeof时可以忽略它们。

3、带虚函数的类

        虚函数放在虚表中,类中定义了虚函数,需要存放一个指向虚表的指针。

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void f(){}  
  6. };  


        上述代码的sizeof结果为sizeof(A.a)+sizeof(指针)。

当有多个虚函数时,仍然只需要一个指向虚表的指针,因此如下代码的结果和上述代码一样:

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void f(){}  
  6.     virtual void f2(){}  
  7.     virtual void f3(){}  
  8. };  

4、普通继承

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5.     char b;  
  6. };  
  7. class B  
  8. {  
  9.     char c;  
  10. };  

上述代码结果取决于编译器,比如在Codeblocks中结果为8,这说明将char c和char b放到一起了,而在VS2010中,结果为12。不过对如下代码:

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. private:  
  6.     char b;  
  7. };  
  8. class B : public A  
  9. {  
  10. public:  
  11.     int d;  
  12.     char c;  
  13.       
  14. };  


Codeblocks和VS2010返回的结果都是12。

一般来说,普通继承的空间计算结果应当是sizeof(基类)+sizeof(派生类),然后考虑对齐,内存空间必须是类中数据成员所占用最大空间的整数倍。不过这是一般情况,具体怎么算要看编译器,比如将上述代码中B的两个数据成员交换位置,结果则可能不同。

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. private:  
  6.     char b;  
  7. };  
  8. class B : public A  
  9. {  
  10. public:   
  11.     char c;  
  12.     int d;  
  13. };  

对于Codeblocks,值不变,仍然为12,但对于VS2010,返回的结果是16。在这点上看,可能Codeblocks为程序分配的的内存要小于VS。

5、普通继承含虚函数的父类

[cpp] view plaincopyprint?
  1. class Test  
  2. {  
  3. public:  
  4.     char a;  
  5.     virtual void get(){}  
  6. };  
  7. class Test2 : public Test  
  8. {  
  9. public:  
  10.     int a;  
  11. };  
求法和上面一样,sizeof(基类)+sizeof(派生类),考虑对齐。上述代码在Codeblocks和VS2010下都是12。

6、含虚函数的子类普通继承含虚函数的父类

[cpp] view plaincopyprint?
  1. class Test  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void get(){}  
  6. };  
  7. class Test2 : public Test  
  8. {  
  9. public:  
  10.     int a;  
  11.     virtual void set(){}  
  12. };  
这个要注意的一点是,虽然子类和父类都包含虚函数, 但它们存放于同一个虚表中,因此只需要一个指针,因而结果是sizeof(基类)+sizeof(派生类)-sizeof(指针)。上述代码在Codeblocks和VS2010下都是12。

如果子类或父类中有多个虚函数,怎么计算呢?按照3中所述,结果不变,如下代码证明这一点

[cpp] view plaincopyprint?
  1. class Test  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void get(){}  
  6.     virtual void ge2(){}  
  7.     virtual void get3(){}  
  8. };  
  9. class Test2 : public Test  
  10. {  
  11. public:  
  12.     int a;  
  13.     virtual void set(){}  
  14. };  
在Codeblocks和VS2010下都是12。

[cpp] view plaincopyprint?
  1. class Test  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void get(){}  
  6. };  
  7. class Test2 : public Test  
  8. {  
  9. public:  
  10.     int a;  
  11.     virtual void set(){}  
  12.     virtual void set2(){}  
  13.     virtual void set3(){}  
  14. };  
仍然是12。

[cpp] view plaincopyprint?
  1. class Test  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void get(){}  
  6.     virtual void ge2(){}  
  7.     virtual void get3(){}  
  8. };  
  9. class Test2 : public Test  
  10. {  
  11. public:  
  12.     int a;  
  13.     virtual void set(){}  
  14.     virtual void set2(){}  
  15.     virtual void set3(){}  
  16. };  
结果一样。

结论,在普通继承下,不管是子类还是父类包含虚函数,子类的sizeof=sizeof(基类数据成员)+sizeof(派生类数据成员)+sizeof(指针)。

7、子类虚继承父类

虚继承比较特别,一般的计算公式应当是sizeof(子类)=sizeof(基类)+sizeof(虚表指针)+sizeof(子类数据成员)。

如下代码是普通虚继承,不带虚函数:

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. };  
  6. class B : virtual public A  
  7. {  
  8. public:   
  9.     int b;  
  10. };  

上述代码在Codeblocks和VS2010下sizeof结果都是12,基类A大小为4,子类B数据成员为4,因为是虚继承,可能需要一个指向虚基类的指针,再占用4字节,得到12。

然而,对于包含虚函数的子类虚继承不含虚函数的父类时,结果发生了变化:

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. };  
  6. class B : virtual public A  
  7. {  
  8. public:  
  9.     int a;  
  10.     virtual void set(){}  
  11. };  

在Codeblocks下结果仍为12,VS2010下却为16。子类增加了一个虚函数,Codeblocks下结果不变,在VS下结果却发生了变化。

可以这样认为,在VS下,计算公式可能如下:sizeof(子类)=sizeof(基类)+sizeof(指示虚基类的指针)+sizeof(子类数据成员)+sizeof(虚表指针)。在CB下可能对内存进行了优化(个人推测)

对于子类和父类中都含虚函数的虚继承,结果如下:

[cpp] view plaincopyprint?
  1. class Test  
  2. {  
  3. public:  
  4.     int a;  
  5.     virtual void get(){}  
  6. };  
  7. class Test2 : virtual Test  
  8. {  
  9. public:  
  10.     int a;  
  11.     virtual void set(){}  
  12. };  
在Codeblocks下结果为16,VS2010下为20。

这是因为,使用了虚继承,子类和父类的虚函数存放在不同的虚表中,因此子类和父类都需要一个指向虚表的指针。

8、多重继承

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. };  
  6. class B :  public A  
  7. {  
  8. public:   
  9.     int b;  
  10. };  
  11. class C : public A  
  12. {  
  13. public:  
  14.     int c;  
  15. };  
  16. class D : public B, public C  
  17. {  
  18. public:  
  19.     int d;  
  20. };  

这个简单,就是基类的简单相加。sizeof(D)=sizeof(B)+sizeof(C),在CB和VS下都是20。

9、多重虚继承

虚继承存在的意义就是为了减少内存开销和二义性,实现对象共享。

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. public:  
  4.     int a;  
  5. };  
  6. class B :  virtual public A  
  7. {  
  8. public:   
  9.     int b;  
  10. };  
  11. class C : virtual public A  
  12. {  
  13. public:  
  14.     int c;  
  15. };  
  16. class D : public B, public C  
  17. {  
  18. public:  
  19.     int d;  
  20. };  

sizeof(D)在CB和VS下都是24。D中包含a,b,c,d四个数据成员,还包含两个指向虚基类A的指针,这种情况下,VS和CB都没有将两个指针合为一个。这种情况也可以这样考虑,sizeof(D)=sizeof(B)+sizeof(C),但由于是虚继承,虚基类A中数据成员a只需要保留一份,而我们算了两次,所以还需要减去A的数据成员,所以公式应当是sizeof(D)=sizeof(B)+sizeof(C)-sizeof(A的非静态数据成员)。

0 0
原创粉丝点击