对象的内存模型

1. C++的内存格局

   C++程序的内存格局通常分为四个区:

   a. 全局数据区(data area):存放全局变量,静态数据和常量。已初始化的全局变量保存在.data段,未初始化的全局变量保存在.bss段中。

   b. 代码区(code area):存放所有类成员函数和非成员函数的代码,该区域是只读的。

   c. 栈区(stack area):存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

   d. 堆区(heap area):除a,b,c后,余下的空间都被称为堆区。

   物理内存中的这些分区大小有限,所以编译器编译出来的可执行文件是一个叫虚拟内存空间的东西,一般大小为4G。

   

2. C++类的组成

   C++类中有两种成员数据:static、nonstatic;三种成员函数:static、nonstatic、virtual。

   

   C++类对象中的成员变量和成员函数是分开存储的,其中:

   a. 成员变量:

      普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式。每个对象都独占一份成员变量。

      静态成员变量:存储于全局数据区中。

   b. 成员函数:存储于代码段中,只用一段空间来存放这个共同的函数代码段,在调用各对象的函数时,都去调用这个公用的函数代码。

      既然很多对象共用一块代码?代码是如何区分具体对象呢?如何知道函数内操作的是谁的成员变量呢?

      C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。函数调用的时候,会传入该对象的指针,自然就知道访问谁的数据成员。

      静态成员函数和非静态成员函数的区别也仅仅是在于这个this指针参数,静态成员函数是没有这个参数的。

 

3. 对象的大小

   每个对象所占用的存储空间只是该对象的数据部分(虚函数指针和虚基类指针也属于数据部分)所占用的存储空间,

   而成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的。所以说new一个对象其实只是new出了成员变量。

   如下代码输出皆为12。

class C1
{
public:
    int i, j, k; // 4 + 4 + 4 = 12
}; 

class C2
{
public:
    int i, j, k; 
    static int m;

public:
    int GetK() const { return k; }
    void SetK(int val) { k = val; }
};

int main()
{
    printf("c1:%d \n", sizeof(C1));
    printf("c2:%d \n", sizeof(C2));
    return 0;
}

 

 4. 如果类中有虚成员函数,那虚函数表存放在哪里呢?

   a. 虚函数表类似一个数组,类对象中存储vptr指针,指向虚函数表.即虚函数表不是函数,不是程序代码,肯定不能存储在代码段。

   b. 虚函数表存储虚函数的地址即虚函数表的元素是指向类虚成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小

      可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不在堆中。

   所以:函数表和静态成员变量一样,存放在全局数据区。

 

5. 派生类对象如何构建

   基类中的数据成员,通过继承成为派生类对象的一部分,需要在构造派生类对象的过程中调用基类构造函数来正确初始化;

   注意这里并没有创建基类对象,调用基类的构造函数是为了初始化派生类对象中的基类成员。

   派生类会全部继承基类的成员变量(会开辟内存空间),但是对于基类中的private成员,派生类是无权访问的,只能调用基类的接口来访问。

   C++标准并没有明确规定派生类的对象在内存中如何分布,来自基类的成员和派生类独有的成员在内存中不一定连续存储。

   贴一个例子,右图是派生类对象逻辑上的布局:

       

 

 

posted @ 2020-05-21 11:28  _yanghh  阅读(641)  评论(0编辑  收藏  举报