class占用内存问题

一、背景知识

1:首先遵从内存对齐规则

2:只有虚函数会占4个字节,其他的函数不占内存;无论多少个虚函数,只有这一个指针,4字节。//注意一般的函数是没有这个指针的,而且也不占类的内存;

3、静态变量由于属于所有类对象共同所有,所以不占内存

二、典型的计算类的占用内存的问题

1空类的问题

class CBase 

}; 
sizeof(CBase)=1;

原始的C结构经过改造,成了面向对象世界的基石——类。除了成员变量外,C++类还可以封装成员函数和其他东西。然而除非 为了实现虚函数和虚继承引入的隐藏成员变量外,C++类实例的大小完全取决于一个类及其基类的成员变量!成员函数基本上不影响类实例的大小。内联函数也不会影响类的大小。

例子:

  1. struct  B {  
  2. public :  
  3.    int  bm1;  
  4. protected :  
  5.    int  bm2;  
  6. private :  
  7.    int  bm3;  
  8.    static   int  bsm;  
  9.    void  bf();  
  10.    static   void  bsf();  
  11.    typedef   void * bpv;  
  12.    struct  N { };  

attention:1)与空的类一样,如果单独衡量一个空类,他所占的内存时1;但是一旦是在类内定义另外一个空类,不占内存,结构体也一样。所以上面答案12;struct N{}并不影响大小

                    2)静态变量存储在全局变量区,不占类的内存

2、继承导致的类的内存分配问题

当父类中含有虚函数时,需要注意的是,每个用有虚函数的父类各自拥有一个虚指针,但是如果自己在本省再有虚函数,就不会在增加虚指针了。下面会介绍具体内存分配示意图来解释

例子:

 
  1. class q

  2. {

  3. virtual int change();

  4. };

  5. class r:public q

  6. {

  7. int a;

  8. };

  9. class g:public q,public r

  10. {

 
  1. virtual int ff();

  2. };

上面例子sizeof(g)=12

1)一个类无论存在几个虚函数,只保存一个虚表指针;

2)非虚继承时,每个父类各自拥有一个虚表指针,但是派生类无论是重载虚函数还是定义新的虚函数,都不会在增加新的虚表指针,

 如果是重载放在对应函数的位置,不是重载而是定义新的放在第一个父类虚表指向内存的后面

3.虚继承导致的内存问题

 我们可以得到如下关于VC++虚继承下内存布局的结论:
1 首先排列非虚继承的基类实例;
2 有虚基类时,为每个基类增加一个隐藏的vbptr,除非已经从非虚继承的类那里继承了一个vbptr;注意这句话的含义,下面进行解释
3 排列派生类的新数据成员;
4 在实例最后,排列每个虚基类的一个实例。

该布局安排使得虚基类的位置随着派生类的不同而“浮动不定”,但是,非虚基类因此也就凑在一起,彼此的偏移量固定不变。

再有虚继承是,一定要记住上述的规则,但是要注意第二天限制的是在此虚继承是否还要生产虚继承的指针原来已经产生的不会消失

例子

 1 C类会保存两份A,因为直接继承A时,不是虚继承,这种方式是不好的

2 F类A只保存一份,而且已经非虚继承了一个虚继承指针,不会再因为virtual A又产生一个

三、总结

        C++程序的内存格局通常分为四个区:全局数据区(data area),代码区(code area),栈区(stack area),堆区(heap area)(即自由存储区)。全局数据区存放全局变量,静态数据和常量;所有类成员函数和非成员函数代码存放在代码区;为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区;余下的空间都被称为堆区。根据这个解释,我们可以得知在类的定义时,类成员函数是被放在代码区,而类的静态成员变量在类定义时就已经在全局数据区分配了内存,因而它是属于类的。对于非静态成员变量,我们是在类的实例化过程中(构造对象)才在栈区或者堆区为其分配内存,是为每个对象生成一个拷贝,所以它是属于对象的。

        应当说明,常说的“某某对象的成员函数”,是从逻辑的角度而言的,而成员函数的存储方式,是从物理的角度而言的,二者是不矛盾的。

        下面我们再来讨论下类的静态成员函数和非静态成员函数的区别:静态成员函数和非静态成员函数都是在类的定义时放在内存的代码区的,因而可以说它们都是属于类的,但是类为什么只能直接调用静态类成员函数,而非静态类成员函数(即使函数没有参数)只有类对象才能调用呢?原因是类的非静态类成员函数其实都内含了一个指向类对象的指针型参数(即this指针),因而只有类对象才能调用(此时this指针有实值)

类中包括成员变量和成员函数。new出来的只是成员变量,成员函数始终存在,所以如果成员函数未使用任何成员变量的话,不管是不是static的,都能正常工作。需要注意的是,虽然调用不同对象的成员函数时都是执行同一段函数代码,但是执行结果一般是不相同的。不同的对象使用的是同一个函数代码段,它怎么能够分别对不同对象中的数据进行操作呢?原来C++为此专门设立了一个名为this的指针,用来指向不同的对象。

        需要说明,不论成员函数在类内定义还是在类外定义,成员函数的代码段都用同一种方式存储。不要将成员函数的这种存储方式和inline(内联)函数的概念混淆。不要误以为用inline声明(或默认为inline)的成员函数,其代码段占用对象的存储空间,而不用inline声明的成员函数,其代码段不占用对象的存储空间。不论是否用inline声明(或默认为inline),成员函数的代码段都不占用对象的存储空间。用inline声明的作用是在调用该函数时,将函数的代码段复制插人到函数调用点,而若不用inline声明,在调用该函数时,流程转去函数代码段的入口地址,在执行完该函数代码段后,流程返回函数调用点。inline与成员函数是否占用对象的存储空间无关,它们不属于同一个问題,不应搞混。

 

1 疑问如下: 
(1)C++中,应该是对象才会被分配内存空间吧??为什么类会有t内存大小!难道还没实例化的时候,类就 已经有了内存空间了? 

(2)函数难道不占用内存空间吗?至少应该放个函数指针在里面的吧?内存是怎样布局的?

(3)静态成员应该是属于类的,怎么类的大小中没有包含静态成员的大小?

下面分别解答如下:
1)一个类定义了,它所占的内存编译器就已经知道了,这时只是得到它占用的大小,并没有分配内存操作 。也可以这样想:编译器肯定知道大小了,这与分配内存空间无关,知道大小了,以后实例化了才能知道要分配多大。
2)类的普通成员、静态成员函数是不占类内存的,至于你说的函数指针在你的类中有虚函数的时候存在一个虚函数表指针,也就是说如果你的类里有虚函数则 sizeof(CObject)的值会增加4个字节。
其实类的成员函数 实际上与 普通的全局函数一样。 
只不过编译器在编译的时候,会在成员函数上加一个参数,传入这个对象的指针。
成员函数地址是全局已知的,对象的内存空间里根本无须保存成员函数地址。 
对成员函数(非虚函数)的调用在编译时就确定了。 
像 myObject.Fun() 这样的调用会被编译成形如 _CObject_Fun( &myObject ) 的样子。
函数是不算到sizeof中的,因为函数是代码,被各个对象共用,跟数据处理方式不同。对象中不必有函数指针,因为对象没必要知道它的各个函数的地址(调 用函数的是其他代码而不是该对象)。 
类的属性是指类的数据成员,他们是实例化一个对象时就为数据成员分配内存了,而且每个对象的数据成员是对立的,而成员函数是共有的~ 
静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员。总之,程序中的所有函数都是位于代码区的。
3)静态成员并不属于某个对象,sizeof取的是对象大小。

2 计算类所占内存的规则

A:空类的所占的内存为1 

B:static修饰的变量和函数不占内存,因为static修饰的成员保存在静态变量区,声明周期一直存在,为所以类的对象共享

C:普通函数不占内存,实质是全局函数

D:virtual修饰的函数会产生一个指向虚表的指针,但是要谨记:1)一一个类无论存在几个虚函数,只保存一个虚表指针2)非虚继承时,每个父类各自拥有一个虚表指针,但是派生类无论是重载虚函数还是定义新的虚函数,都不会在增加新的虚表指针3)虚继承时,首先增加虚继承的指针,然后当派生类是重载虚函数时,不增加新的虚表指针,当定义新的虚函数时,增加虚表指针

E static和virtual不能一起使用

   因为静态成员函数,可以不通过对象来调用,即没有隐藏的this指针。virtual函数一定要通过对象来调用,即有隐藏的this指针。static function都是静态决议的(编译的时候就绑定了)而virtual function 是动态决议的(运行时候才绑定)

3 内存分配图

1)继承时,无覆盖

2)继承时,有覆盖

3)多重继承,无覆盖

4)多重继承,有覆盖

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值