类中内容在内存中到底是如何分配的呢?


分类:

      一个类,有成员变量:静态与非静态之分;而成员函数有三种:静态的、非静态的、虚的。
      那么这些个东西在内存中到底是如何分配的呢?
      以一个例子来说明:

      [html]view plaincopyprint?

  1.       #include"iostream.h"  
  2.       class CObject  
  3.       {  
  4.       public:  
  5.       static int a;  
  6.       CObject();  
  7.       ~CObject();  
  8.       void Fun();  
  9.       
  10.       private:  
  11.       int m_count;  
  12.       int m_index;  
  13.       };  
  14.       void CObject::Fun()  
  15.       {  
  16.       cout<<"Fun\n"<<endl;  
  17.       }  
  18.       CObject::CObject()  
  19.       {  
  20.       cout<<"Construct!\n";  
  21.       }  
  22.       CObject::~CObject()  
  23.       {  
  24.       cout<<"Destruct!\n";  
  25.       }  
  26.       int CObject::a=1;  
  27.       void main()  
  28.       {  
  29.       cout<<"Sizeof(CObject):"<<sizeof(CObject)<<endl;  
  30.       //CObject::Fun();  
  31.       cout<<"CObject::a="<<CObject::a<<endl;  
  32.       CObject myObject;  
  33.       cout<<"sizeof(myObject):"<<sizeof(myObject)<<endl;  
  34.       cout<<"sizeof(int)"<<sizeof(int)<<endl;  
  35.       
  36.       }  
这是我的一段测试代码, 
      运行结果是: 
      Sizeof(CObject):8 
      CObject::a=1 
      Construct! 
      sizeof(myObject):8 
      sizeof(int)4 
      Destruct! 
      我有疑问如下: 
      (1) C++中,应该是对象才会被分配内存空间吧??为什么CObject内存大小是8,刚好和两个 成员变量的大小之和一致!难道还没实例化的时候,类就 已经有了内存空间了? 
      (2)当对象生成了之后,算出的内存大小怎么还是8,函数难道不占用内存空间吗?至少应该放个 函数指针在里面的吧?内存是怎样布局的?
      (3) 静态成员应该是属于类的,怎么类的大小中没有包含静态成员的大小?
      下面分别解答如下:
      1)Sizeof(CObject)是在编译时就计算了的,一个类定义了,它所占的内存编译器就已经知道了,这时只是得到它占用的大小,并没有分配内存操作 。也可以这样想:编译器肯定知道大小了,这与分配内存空间无关,知道大小了,以后实例化了才能知道要分配多大。
      2)类的普通成员、静态成员函数是不占类内存的,至于你说的函数指针在你的类中有 虚函数的时候存在一个 虚函数表指针,也就是说如果你的类里有虚函数则 sizeof(CObject)的值会增加4个字节。
      其实 类的成员函数 实际上与 普通的 全局函数一样。 
      只不过编译器在编译的时候,会在成员函数上加一个参数,传入这个对象的指针。
      成员函数地址是全局已知的,对象的内存空间里根本无须保存成员函数地址。 
      对成员函数(非虚函数)的调用在编译时就确定了。 
      像 myObject.Fun() 这样的调用会被编译成形如 _CObject_Fun( &myObject ) 的样子。
      函数是不算到sizeof中的,因为函数是代码,被各个对象共用,跟数据处理方式不同。对象中不必有函数指针,因为对象没必要知道它的各个函数的地址(调 用函数的是其他代码而不是该对象)。 
      类的属性是指类的数据成员,他们是实例化一个对象时就为数据成员分配内存了,而且每个对象的数据成员是对立的,而成员函数是共有的~ 
      静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员。总之,程序中的所有函数都是位于代码区的。
      3)静态成员并不属于某个对象,sizeof取的是对象大小。
      知道了上面的时候,就可以改一下来看看:
      我也补充一些: 
      class CObject 
      { 
      public: 
      static int a; 
      CObject(); 
      ~CObject(); 
      void Fun(); 
      private: 
      double m_count;  //这里改成了double 
      int  m_index; 
      }; 
      这个类用sizeof()测出来的大小是 2*sizeof(double)=16 
      class CObject 
      { 
      public: 
      static int a; 
      CObject(); 
      ~CObject(); 
      void Fun(); 
      private: 
      char m_count;  //这里改成了char 
      int  m_index; 
      }; 
      大小是2*sizeof(int)=8 
      class CObject 
      { 
      public: 
      static int a; 
      CObject(); 
      ~CObject(); 
      void Fun(); 
      private: 
      double m_count;  //这里改成了double 
      int  m_index; 
      char  c; 
      }; 
      sizeof(char)+sizeof(int) <sizeof(double) 所以大小是2*sizeof(double) 
      其实这里还有一个是内存对齐的问题。
      空类大小是1。 
      另外要注意的一些问题:

      先看一个空的类占多少空间?

      class Base
      {
      public:
      Base();
      ~Base();
      }; 

class Base { public: Base(); ~Base(); };

      注意到我这里显示声明了构造跟析构,但是sizeof(Base)的结果是1.

      因为一个空类也要实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含 的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。

      而析构函数,跟构造函数这些成员函数,是跟sizeof无关的,也不难理解因为我们的sizeof是针对实例,而普通成员函数,是针对类体的,一个类的成 员函数,多个实例也共用相同的函数指针,所以自然不能归为实例的大小,这在我的另一篇博文有提到。

      接着看下面一段代码

      [html]view plaincopyprint?

  1.       class Base   
  2.       {   
  3.       public:   
  4.       Base();                   
  5.       virtual ~Base();         //每个实例都有虚函数表   
  6.       void set_num(int num)    // 普通成员函数,为各实例公有,不归入sizeof统计   
  7.       {   
  8.       a=num;   
  9.       }   
  10.       private:   
  11.       int  a;                  //占4字节   
  12.       char *p;                 //4字节指针   
  13.       };   
  14.       
  15.       class Derive:public Base   
  16.       {   
  17.       public:   
  18.       Derive():Base(){};         
  19.       ~Derive(){};   
  20.       private:   
  21.       static int st;         //非实例独占   
  22.       int  d;                     //占4字节   
  23.       char *p;                    //4字节指针   
  24.       
  25.       };   
  26.       
  27.       int main()     
  28.       {     
  29.       cout<<sizeof(Base)<<endl;   
  30.       cout<<sizeof(Derive)<<endl;   
  31.       return 0;   
  32.       }   
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;

  1.       class Derive:public Base 
  2.       { 
  3.       public: 
  4.       Derive():Base(){}; 
  5.       ~Derive(){}; 
  6.       private: 
  7.       static int st; 
  8.       int  d; 
  9.       char *p; 
  10.       char c; 
  11.       
  12.       }; 
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字节对齐的,调整规则。

      转载自:http://www.blue1000.com/bkhtml/c151/2010-11/69613.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值