C++对象的内存分配是一次性完成的,类对象在分配内存时会一次性分配整个对象的内存空间。然后再逐步进行类的初始化。

一、构造(初始化)顺序

C++对象的构造顺序是:

  1. 基类对象
  2. 成员对象
  3. 对象自身

这种构造顺序的考虑是多方面的,,主要有以下几种考虑。

1,考虑到资源的依赖关系。被别的资源依赖的对象总是要最先构造而最后析构的。因为它要在别的资源构造之前就被构造,保证别的资源构造时能够访问到它。它也要在别的资源被析构后才能被析构,保证别的资源在析构时也能够访问到它。栈中对象的生命周期的管理,也是先进后出,也是基于这样的考虑。

构建C++类对象时,先构造底层的依赖,再构造上层的对象。因为上层的对象在构造函数中,可能使用到底层的依赖对象,此时如果底层依赖对象还未初始化,会导致的未定义的行为。

C++成员对象可能会依赖基类对象的资源(构造时,需要使用基类对象的数据或虚函数来作为构造参数),C++对象依赖基类对象和成员对象的资源,所以,先构造基类对象,再成员对象,再对象自身。

2,在内存布局上,也是先是排布基类对象,再成员对象,从而构成整个派生类对象。

3,在概念上,应该先建立IS-A关系,再建立HAS-A关系。从面向对象的角度,基类代表了对象的“本质”,而成员对象是“组成部分”。先有本质,再有组成,也是一种逻辑顺序。

多重继承时,基类的构造顺序是按照继承顺序来的。

成员对象构造或初始化顺序,也是按照它们在类内的声明的顺序来的。

二,析构(反初始化)顺序

析构的顺序与构造的顺序必然是相反的,这样才能保证在析构函数中,每个成员变量、基类对象仍然可以被安全地访问。

所以,先析构最上层,再析构下层的依赖。避免因为下层依赖被析构后,上层的析构函数使用了下层的依赖,导致的未定义行为。析构的顺序为:

  1. 对象自身
  2. 成员对象
  3. 基类对象

三,对象的构造过程

当一个对象被创建时,会先分配内存,再进行初始化/构造:

1. 分配内存:一次性分配整个对象的内存空间,包括所有基类子对象和成员对象的内存。内存布局和大小在编译时确定。
2. 构造基类子对象:按照继承顺序,在对象内存的对应偏移位置构造基类对象,调用基类的构造函数。
3. 构造/初始化成员对象:按照声明顺序,在对象内存的对应偏移位置构造成员对象,调用成员对象的构造函数。
4. 执行派生类构造函数体:调用派生类自己的构造函数,执行构造函数体内的代码。

class MyClass
{
public:
	// 初始化a时,使用到b。此时能够访问到b的内存,因为此时内存已经分配。、
	// 但是b还没有初始化,访问到的b内存中的未初始化的数据,
	// 比如a可能被初始化为-858993460
	int a = b; 
	int b = 12;
};

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐