类的内存分配

#include<iostream>
using namespace std;
class classA
 {
    private:
        intA;
        intB;
 
    voidprin1() {}
 
    voidprin2() {}
 
    virtualvoid prin3() {    }
};
 
class ClassB : public ClassA
 {
    public:
        intC;
        intD;
 voidprin4() {    }
 
    voidprin5() {    }
 
    virtual void  prin6() {    }
};
 
intmain(intargc, char*
 argv[]) {
    cout<<sizeof(ClassA)<<endl;       // 16
    cout<<sizeof(ClassB)<<endl;       //24
   return0;
    }

测试环境:

Ubuntu 12.04.5 LTS x86_64 GNU/Linux

g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 编译参数 -m64

结果是

homer@homer-pc:~/Desktop$ g++ testMem.c -o testMem && ./testMem
16

24

结果为什么是这样?

32位系统int占4个字节,64位系统int也占4个字节(不是8个字节),而一个类中所有的虚函数通过一个虚函数指针管理,类对象的大小只包含这个vptr指针(在64位机器上指针sizeof为8个字节),其他虚函数是放在别的内存空间中管理,vptr指针在64位机器上是8个字节大小(32位机器上是4个字节)。注意到普通成员函数并不占类对象的大小空间,因为普通成员函数通过this指针管理,一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。

sizeof(ClassA)

1)int A 和 int B 各占4个字节,考虑64位机器编译器对其规则,合并为8个字节

2)virtual void prin3() 虚函数的vptr指针,在64位机器编译器上占8个字节

3)合计 sizeof(ClassA)为 8 + 8 = 16个字节

this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。这个this指针会因编译器不同而有不同的放置位置,可能是栈,也可能是寄存器,甚至全局变量。

子类其实不管如何继承,用sizeof()算该类的大小都会把父类中的私有成员变量所占的空间算进去,也就是说,私有变量也在子类中分配了内存,但你却不可以直接访问,这起到一个保护作用,这如同一个珠宝,共有继承就是开放性的展览,而私有继承是把珠宝锁起来,你却不能动,要动珠宝如果有管家(基类的public中定义了一些对其私有变量操作的成员函数,)只能让管家帮你代劳。

sizeof(ClassB)

1)int A 和 int B 各占4个字节,是父类ClassA中的私有变量,合并占用8个字节

2)virtual void prin3() 虚函数的vptr指针,父类ClassA在子类中不会分配空间

3)int C 和 int D 各占4个字节,在子类中会分配空间,合并占用8个字节

4)virtual void prin6() 虚函数的vptr指针,在64位机器编译器上占8个字节

5)合计 sizeof(ClassB)为 8 + 8 + 8 = 24个字节

注明: 上述示例在32位编译器上测试,sizeof(ClassA)和sizeof(ClassB)分别为12和20字节(虚函数指针占用空间少了4字节)

只有虚函数会占用一个指针大小的内存,原因是系统用一个指针维护这个类的虚函数表,并且注意这个虚函数无论含有多少项(类中含有多个虚函数)都不会影响类的大小。

知识延伸:

1) 空 ClassA(验证空Class占用空间大小)

class ClassA
 {
    private:
 
    voidprin1() {     }
 
    voidprin2() {    }
};
 cout<<sizeof(ClassA)<<endl;       // 1

2) ClassA 只有一个char(验证编译器对其规则)

class ClassA
 {
    private:
        charC;
 
    voidprin1() {     }
 
    voidprin2() {    }
};
 
cout<<sizeof(ClassA)<<endl;       // 1

3)ClassA 有一个char和一个虚函数(或一个long型)(验证编译器对其规则二)

classClassA
 {
    private:
        charC;
 
    voidprin1() {     }
 
    voidprin2() {    }
 
    virtualvoid prin3() {    }
};
 
cout<<sizeof(ClassA)<<endl;       // 16

4)ClassA 有一个char和一个虚函数(或一个long型),且对调char和虚函数的先后顺序(验证编译器对其规则三)

class ClassA
 {
 
    voidprin1() {     }
 
    voidprin2() {    }
 
    virtualvoid prin3() {    }
 
    private:
        charC;
};
 cout<<sizeof(ClassA)<<endl;       // 16

5)ClassA 有一个char,一个int,一个虚函数(或一个long型)(验证编译器对其规则四)

class ClassA
 {
    private:
        charC;
        intA;
 
    voidprin1() {     }
 
    voidprin2() {    }
 
    virtualvoid prin3() {    }
};
 cout<<sizeof(ClassA)<<endl;       // 16

6)ClassA 有一个char,一个int(验证编译器对其规则五)

class ClassA
 {
    private:
        charC;
        intA;
 
    voidprin1() {     }
 
    voidprin2() {    }
};
 cout<<sizeof(ClassA)<<endl;       // 8

7)ClassA 只有一个int(验证编译器对其规则六)

class ClassA
 {
    private:
        intA;
 
    voidprin1() {     }
 
    voidprin2() {    }
};
 cout<<sizeof(ClassA)<<endl;       // 4

总结如下:

1) 空类的 sizeof 为1个字节

2) 只有一个char的类,sizeof为一个字节

3) 类中含有char和虚函数,将以最大的变量或指针为编译器对齐规则,例如:虚函数指针占8个字节(64位编译器),则char虽然只占1个字节,但对齐后空余了7个字节,合并类占8(指针) + 1(char) + 7(对齐的空字节) = 16个字节

4) 对齐规则,跟变量或虚函数的先后顺序无关,只跟最大变量类型或函数指针有关,函数指针跟编译器最大对齐位数有关(不太好理解,请继续往下看)

5) char和int合占8个字节,虚函数指针占8个字节,且以最大的虚函数指针的8字节对其,其中char占一个空余3个字节合并占4个,int占4个字节,按8位规则对齐,合计16字节

6) 一个char和int合并占8个字节,无虚函数,此时以最大变量类型int对齐,因此char占1字节空3字节占4字节

7)一个int占4位,自己便是最大的对齐规则; 2)一个char同理也占1个字节,此处跟编译器最大64位对齐规则无关(即一个int或一个char,不会拓展到占用8字节)

这里,又延伸出了一个很有意思的问题,空类sizeof为什么不为0,而为1?

回答这个问题,需要回到编译器和类的实例化问题上来,在学校看过《深度探索C++对象模型》,类分抽象类和普通类,空类属于普通类,是可以被实例化的。

每个类的实例,在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址,因此1)中的空类ClassA默认会占用1个字节。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值