C++对象模型浅析

来源:互联网 发布:js动态删除指定tr 编辑:程序博客网 时间:2024/06/18 06:16

本文仅代表博主自己对C++内存对象模型的一点理解,如果文中有

理解偏差和不准确的地方,希望各位大大提出,我好及时改正。

本博文只对博主自己负责,不对任何人负责。

 

就如《深度探索C++对象模型》一书中介绍的C++的封装并没有给C++带来过多的开销。然而面向对象的编程方法却给广大的编程者提供了一种更为开阔的编程思路。

 

好,我们主要看看前面一句。开销是什么,这里的开销主要指C++类所占内存的空间。首先,我们看这样一个例子,我们定义一个结构体和类,结构体和类中含有相同的数据成员。除此之外再无其他。我们看看这个结构体和类的大小是多少:

复制代码
 1 #include <iostream> 2 using namespace std; 3  4 struct A 5 { 6     int a; 7     int b; 8     int c; 9 };10 11 class B12 {13     int a;14     int b;15     int c;16 };17 18 19 20 int main()21 {22     cout<<"sizeof(A)="<<sizeof(A)<<endl;23     cout<<"sizeof(B)="<<sizeof(B)<<endl;24     return 0;25 }
复制代码

结果:

看来只要数据成员相同,那么就算声明为类,也不会带来额外的内存开销。

 

现在我们再看一个例子,我们让类B更加复杂一下,我们添加一个静态成员变量和,一个静态成员函数,一个非静态成员函数,一个虚函数:我们再看看这个例子的结果将是什么样的结果:

复制代码
 1 #include <iostream> 2 using namespace std; 3  4 struct A 5 { 6     int a; 7     int b; 8     int c; 9 };10 11 class B12 {13 private:14     int a;15     int b;16     int c;17     static int d;18 19 public:20     static void fun1()21     {22         cout<<"This is a static fun1."<<endl;23     }24     25     void fun2()26     {27         cout<<"This is ordinary fun2."<<endl;28     }29 30     virtual void fun3()31     {32         cout<<"This is a virtual fun3."<<endl;33     }34 };35 36 37 38 int main()39 {40     cout<<"sizeof(A)="<<sizeof(A)<<endl;41     cout<<"sizeof(B)="<<sizeof(B)<<endl;42     return 0;43 }
复制代码

 

结果:

在这里我们增加了一个静态数据成员d,一个静态成员函数fun1,非静态的成员函数fun2一个虚函数fun3.不难发现就类的代码规模来说,的确增加了不少,然而结果仅仅比之前增加了四个字节的开销。《深度探索C++对象模型》中有这样一句话说C++封装所带来的开销主要来源于虚函数。

 

那么我们可以先看看结构体A的内存分布:可以产生结构体A的一个对象:

1     A *a;2     a=new A;

对象a的内存结构如下:

显然a中有三个整型数据元素a,b,c刚好12个字节

 

现在我们看看类B的内存分布

可以产生类B的一个对象:

1     B *b;2     b=new B;

对象B的内存结构如下:

可以看到尽管类B封装的很多的内容,但是较之A而言仅仅多了一个__vfptr,我们不禁回忆起前面的一句话,C++的封装的开销主要来自于虚函数。其实如果对C++有一定了解的同学都不难猜出,这个ptr其实就是一个虚表指针。正因为这个虚表指针给封装带来了额外的4个字节的开销。

 

那么虚表指针是什么呢,虚表指针其实就是指向虚函数表的一个指针。当一个类中有虚函数的时候,类会自动生成一个指针,该指针保存的是该类中第一个虚函数的地址。所以就算有多个虚函数仍然只需要保存一个虚表指针,然后通过这个指针逐个遍历就可以取得各个虚函数的地址。然而关于

虚函数在C++中功能特性我们留待下次一起学习了。

这下一目了然了吧。

 

 

那么我们可以总结一下影响类的内存开销主要有以下几个方面:

1.非静态的数据成员

2.虚函数表指针

3.当然既然类也是要考虑内存对齐的。

 

还有几点需要注意:

1.类的静态数据成员存储在全局变量区,不带来C++内存开销,该静态数据成员属于整个类的不属于具体某个对象,其初始化要在类外进行。

2.类的静态成员函数是属于整个类的,不属于某个对象,不会带来内存开销。注意类的静态成员函数中不能调用非静态的成员变量。

3.类的普通成员函数始终在程序的代码区中保存一份,不带来内存开销。

 

 

最后再通过一张图片只管展示结构体A和类B的内存布局:


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 领导要把我调走怎么办 美国上庭遇法官退休案子怎么办 夫妻一套房离婚户囗怎么办 正营生不了副团怎么办 浅色衣服被染色了怎么办 解大便解不出来怎么办 强拆挖掘机被村民扣押怎么办 小孩哭的时候接不上气怎么办 昆明市长信箱发不出去怎么办 中国公民在香港想去澳门怎么办 面包车不给贴膜怎么办 在香港海关被扣怎么办 发现被医院骗了怎么办 白皮子科染上血怎么办 宝宝头发里长湿疹怎么办 婴儿头发里长湿疹怎么办 宝宝解小便地方有湿疹怎么办 婴儿湿疹怎么办长在脸上 广州奥龙堡游泳卡过期了怎么办 大学生在学校当兵户口怎么办 茶叶梗枕头太硬怎么办 茶梗枕头太硬怎么办 照片放久了变红怎么办 乳腺萎缩和韧带松弛怎么办 航海王启航服务器爆满怎么办 LOL记分板没了怎么办 辅导孩子做作业没有耐心怎么办 宝宝住院三天回家不吃母乳怎么办 锁频君把应用变暗了怎么办 95的油加成92的怎么办 倒库一边宽了怎么办 倒库老是倒不好怎么办 倒库方向打早了怎么办 倒库左边小了怎么办 倒车入库小于30公分怎么办 倒库大于30公分怎么办 有行车记录仪遇到碰瓷怎么办 狗换了主人不吃怎么办 遇到扔东西碰瓷怎么办 碰见碰瓷的人怎么办 开店遇上碰瓷的顾客怎么办