本文仅代表博主自己对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 B 12 { 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 B 12 { 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的内存布局: