C++对象模型浅析

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

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

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

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

更为开阔的编程思路。

好,我们主要看看前面一句。开销是什么,这里的开销主要指C++类所占内存的空间。首先,我们看这样一个例子,我们定义一个结构体和类,结

构体和类中含有相同的数据成员。除此之外再无其他。我们看看这个结构体和类的大小是多少:

 #include <iostream>
using namespace std; struct A
{
int a;
int b;
int c;
}; class B
{
int a;
int b;
int c;
}; int main()
{
cout<<"sizeof(A)="<<sizeof(A)<<endl;
cout<<"sizeof(B)="<<sizeof(B)<<endl;
return ;
}

结果:

C++对象模型浅析

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

现在我们再看一个例子,我们让类B更加复杂一下,我们添加一个静态成员变量和,一个静态成员函数,一个非静态成员函数,一个

虚函数:我们再看看这个例子的结果将是什么样的结果:

 #include <iostream>
using namespace std; struct A
{
int a;
int b;
int c;
}; class B
{
private:
int a;
int b;
int c;
static int d; public:
static void fun1()
{
cout<<"This is a static fun1."<<endl;
} void fun2()
{
cout<<"This is ordinary fun2."<<endl;
} virtual void fun3()
{
cout<<"This is a virtual fun3."<<endl;
}
}; int main()
{
cout<<"sizeof(A)="<<sizeof(A)<<endl;
cout<<"sizeof(B)="<<sizeof(B)<<endl;
return ;
}

结果:

C++对象模型浅析

在这里我们增加了一个静态数据成员d,一个静态成员函数fun1,非静态的成员函数fun2一个虚函数fun3.不难发现就类的代码规模

来说,的确增加了不少,然而结果仅仅比之前增加了四个字节的开销。《深度探索C++对象模型》中有这样一句话说C++封装所带来

的开销主要来源于虚函数。

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

     A *a;
a=new A;

对象a的内存结构如下:

C++对象模型浅析

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

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

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

     B *b;
b=new B;

对象B的内存结构如下:

C++对象模型浅析

可以看到尽管类B封装的很多的内容,但是较之A而言仅仅多了一个__vfptr,我们不禁回忆起前面的一句话,C++的封装的开销主要来自于虚

函数。其实如果对C++有一定了解的同学都不难猜出,这个ptr其实就是一个虚表指针。正因为这个虚表指针给封装带来了额外的4个字节的开销。

那么虚表指针是什么呢,虚表指针其实就是指向虚函数表的一个指针。当一个类中有虚函数的时候,类会自动生成一个指针,该指针保存的是该类

中第一个虚函数的地址。所以就算有多个虚函数仍然只需要保存一个虚表指针,然后通过这个指针逐个遍历就可以取得各个虚函数的地址。然而关于

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

C++对象模型浅析

这下一目了然了吧。

 

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

1.非静态的数据成员

2.虚函数表指针

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

还有几点需要注意:

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

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

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

 

 

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

C++对象模型浅析

上一篇:测试环境hbase集群异常


下一篇:复制记录一下MPU