zoukankan      html  css  js  c++  java
  • 关于类对象大小的 sizeof 计算问题

    之前看了很多关于类对象大小问题的计算,今天闲着没事就来整理整理,写写自己的看法。

    首先,来看看一个只有构造函数和析构函数的空类:

    1. #include <iostream>  
    2. using namespace std;  
    3. class Base  
    4. {  
    5. public:  
    6.     Base();  
    7.     ~Base();  
    8. };  
    9. int main(int argc, char *argv[])  
    10. {  
    11.     cout << sizeof(Base) << endl;  
    12. }  

    输出结果为:1

          因为一个空类也要实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。 而析构函数,跟构造函数这些成员函数,是跟sizeof无关的,也不难理解因为我们的sizeof是针对实例,而普通成员函数,是针对类体的,一个类的成员函数,多个实例也共用相同的函数指针,所以自然不能归为实例的大小。

         如果给这个类添加成员变量,最后输出的大小就是这些成员变量的大小之和(这里涉及到一个成员对齐问题,不再叙述了)。

    接下来再来看一个有继承的例子:

    1. #include <iostream>  
    2. using namespace std;  
    3. class Base  
    4. {  
    5. public:  
    6.     Base();                  
    7.     ~Base();           
    8.     void set_num(int num)    //普通成员函数  
    9.     {  
    10.         a=num;  
    11.     }  
    12. private:  
    13.     int  a;                  //占4字节  
    14.     char *p;                 //4字节指针  
    15. };  
    16. class Derive:public Base  
    17. {  
    18. public:  
    19.     Derive():Base(){};       
    20.     ~Derive(){};  
    21. private:  
    22.     static int st;         //非实例独占  
    23.         int  d;                //占4字节  
    24. };  
    25. int main(int argc, char *argv[])   
    26. {   
    27.     cout<<sizeof(Base)<<endl;  
    28.     cout<<sizeof(Derive)<<endl;  
    29.     return 0;  
    30. }  

    输出结果为:8   12

    结果很显然, Base 类按4字节对齐,所以是8个字节,Derive 类中不但继承了Base 类的两个成员变量,还多了两个成员变量,但大小却只有12字节,可以得出:静态变量在计算时是不做考虑的。

    上面的例子中都没有涉及到虚函数,下面看个有虚函数的例子:

    1. #include <iostream>  
    2. using namespace std;  
    3. class Base  
    4. {  
    5. public:  
    6.     Base() {}  
    7.     virtual ~Base() {}  
    8. };  
    9. int main(int argc, char *argv[])  
    10. {  
    11.     cout << sizeof(Base) << endl;  
    12.     return 0;  
    13. }  

    输出结果为:4

          和第一个程序相比,这个类中,析构函数变成了虚函数,类的大小也变成了4字节,这是因为有了虚函数,编译器就会为类创建一个虚函数表(vtable),并创建一个指针(vptr)指向这个虚函数表。所以类大小变为4字节。如果在 Base 类中再添加新的虚函数,该类的大小还是不会变,因为指向虚函数的指针是放在虚函数表中的,指向虚函数表的指针不会变。

    如果在这个类中添加数据成员,就会在4字节的基础上对象大小。

    下面再来看看虚函数的继承问题:

    1. #include <iostream>  
    2. using namespace std;  
    3. class Base  
    4. {  
    5. public:  
    6.     Base();                  
    7.     virtual ~Base();           
    8.     void set_num(int num)    //普通成员函数  
    9.     {  
    10.         a=num;  
    11.     }  
    12. private:  
    13.     int  a;                  //占4字节  
    14.     char *p;                 //4字节指针  
    15. };  
    16. class Derive:public Base  
    17. {  
    18. public:  
    19.     Derive():Base(){};       
    20.     ~Derive(){};  
    21.     virtual void foo() { }  
    22. private:  
    23.     static int st;         //非实例独占  
    24.         int  d;                //占4字节  
    25. };  
    26. int main(int argc, char *argv[])   
    27. {   
    28.     cout<<sizeof(Base)<<endl;  
    29.     cout<<sizeof(Derive)<<endl;  
    30.     return 0;  
    31. }  

    输出结果为:12    16

          Base类的大小为12字节很显然,Derive 类中,虽然有一个虚函数 foo ,但是因为它是从Base 类继承的,所以也继承了其虚函数表,并没有创新新的虚函数表,只是在继承下来的表中添加了一项,所以大小为16字节。

    再来看看一个虚继承的例子:

    1. #include <iostream>  
    2. using namespace std;  
    3. class Base  
    4. {  
    5. public:  
    6.     Base();                  
    7.     virtual ~Base();           
    8.     void set_num(int num)    //普通成员函数  
    9.     {  
    10.         a=num;  
    11.     }  
    12. private:  
    13.     int  a;                  //占4字节  
    14.     char *p;                 //4字节指针  
    15. };  
    16. class Derive:virtual public Base  
    17. {  
    18. public:  
    19.     Derive():Base(){};       
    20.     ~Derive(){};  
    21.     virtual void foo() { }  
    22. private:  
    23.     static int st;         //非实例独占  
    24.         int  d;                //占4字节  
    25. };  
    26. int main(int argc, char *argv[])   
    27. {   
    28.     cout<<sizeof(Base)<<endl;  
    29.     cout<<sizeof(Derive)<<endl;  
    30.     return 0;  
    31. }  

    输出结果为:12    20

          这里由于虚继承而引入了一个间接的指针(vbc),该指针是指向虚函数表的一个slot,表中存放着该slot中存放虚基类子对象的偏移量的负值。所以大小比之前多了4字节。就算同时虚继承自两个类,也只会有一个这样的间接指针,也就是大小也只多4字节。

    说到这里,关于类对象大小问题的计算应该差不多了。

  • 相关阅读:
    使用某些 DOCTYPE 时会导致 document.body.scrollTop 失效
    VB.NET 笔记1
    知识管理系统Data Solution研发日记之一 场景设计与需求列出
    知识管理系统Data Solution研发日记之五 网页下载,转换,导入
    折腾了这么多年的.NET开发,也只学会了这么几招 软件开发不是生活的全部,但是好的生活全靠它了
    分享制作精良的知识管理系统 博客园博客备份程序 Site Rebuild
    知识管理系统Data Solution研发日记之四 片段式数据解决方案
    知识管理系统Data Solution研发日记之二 应用程序系列
    知识管理系统Data Solution研发日记之七 源代码与解决方案
    知识管理系统Data Solution研发日记之三 文档解决方案
  • 原文地址:https://www.cnblogs.com/iloveyoucc/p/2409084.html
Copyright © 2011-2022 走看看