【C++】构造函数初始化列表 ④ ( 构造函数 和 析构函数 调用顺序分析 )

2023-09-19 20:15:00


构造函数初始化列表 总结 :

  • 初始化列表 可以 为 类的 成员变量 提供初始值 ;
  • 初始化列表 可以 调用 类的 成员变量 类型的 构造函数 进行成员变量初始化操作 ;
  • 初始化列表 可以 使用 构造函数 中传入的 参数 ;
  • 类初始化时 , 根据定义顺序 , 先调用 成员变量的 构造函数 , 然后调用外部类构造函数 , 析构函数正好相反 ;
  • 实例对象 的 const 成员变量 必须只能在 初始化列表 中进行 初始化 , 所有的构造函数都要进行初始化操作 ;




一、构造函数 和 析构函数 调用顺序 说明




1、构造函数调用顺序


在一个类 C 中 , 嵌套了 A 和 B 两个类类型的 对象 作为 成员变量 ;


构造函数的 调用顺序如下 :

  • 先调用 被嵌套类 A 和 B 的构造函数 , 再调用外部 C 类的构造函数 ;
  • A 和 B 构造函数 , 成员变量 中 谁先声明 , 就先调用谁的 构造函数 ;
    • 注意 : A 和 B 在 构造函数 初始化列表 中的顺序 , 与先调用谁的构造函数无关 ;

2、析构函数调用顺序


析构函数调用顺序 与 构造函数调用顺序相反 , 直接 将 构造函数 调用顺序 倒序排列即可 ;


3、拷贝构造函数也可以定义初始化列表


如果一个类 没有定义 无参构造函数 , 只有一个 有参的构造函数 ,

此时 , C++ 编译器 不会为其 生成 默认的无参构造函数 ;


这种场景下 涉及到了 构造函数 的类型 :

  • 强制在初始化列表中调用构造函数 : 如果类中定义了 有参构造函数 , 导致 无参构造函数 被屏蔽 , 那么 在 所有的构造函数的 初始化列表中 , 都必须强制调用 子对象 的 构造函数 ;
  • 不强制在初始化列表中调用构造函数 : 如果类中定义了 无参构造函数 , 或者 有默认的 无参构造函数 , 那么在 初始化列表 中不强制调用 子对象 的构造函数 ;

使用如下方式 , 声明 A 和 B 类型的成员变量 , 会自动调用 默认的无参构造函数 初始化对象 , 但是由于 A 和 B 中定义了 有参构造函数 , 无参构造函数 被屏蔽了 ;

	A m_a;			// A 类型成员变量
	B m_b;			// B 类型成员变量

没有 无参构造函数 , 上面声明的 A 和 B 两个对象便无法创建成功 ;

此时 , 只能在 构造函数的 初始化列表 中 , 调用 A 和 B 的 有参构造函数 创建 A B 两个成员变量 ;


拷贝构造函数 也是 构造函数 , 也必须在 初始化列表 中 调用 构造函数 , 对子对象进行初始化操作 ;





二、构造函数 和 析构函数 调用顺序 代码分析




1、构造函数调用顺序


在下面的代码中 ,

定义了 类 A , 该类实现了 有参构造函数 , 其 无参构造函数 被屏蔽 , 如果要初始化 A 类型的对象 , 必须使用有参构造函数 , 使用 A a 的形式定义的变量 , 无法进行初始化 ;

class A
{
public:
	// 带参构造函数
	A(int age, int height)
	{
		m_age = age;
		m_height = height;
		cout << "执行 A 的构造函数" << endl;
	}

	~A()
	{
		cout << "执行 A 的析构函数" << endl;
	}

public:
	int m_age;		// 年龄
	int m_height;	// 身高
};

定义了 类 B 与 上述 类 A 基本一致 , 也是无法使用 默认的无参构造函数 , 必须调用有参构造函数 ;


定义 类 C , 其中维护了 A 和 B 两个子对象 ,

public:
	int m_age;		// 年龄
	A m_a;			// A 类型成员变量
	B m_b;			// B 类型成员变量
	const int m_const_int;	// 常量成员

由于 A 和 B 都无法使用 无参构造函数 , 因此在 类 C 的所有构造函数 ( 包括 拷贝构造函数 ) 的 初始化列表中 , 必须强制调用 A 和 B 的 有参构造函数 ;

  • 此外由于 还定义了 const int m_const_int 常量成员 , 类 C 的 所有构造函数 ( 包括 拷贝构造函数 ) 的 初始化列表中 , 同时也必须强制对 常量成员进行初始化 ;
	C() : m_age(10), m_b(5, 110), m_a(10, 150), m_const_int(888)
	{}

最终的 构造函数 执行顺序是 :

执行 A 的构造函数
执行 B 的构造函数
执行 C 的构造函数
执行 A 的构造函数
执行 B 的构造函数
执行 C 的 拷贝构造函数

执行

	// 通过 C 的有参构造函数
	// 其中 构造函数中的参数 作为 参数列表 中的参数值
	C c(10, 10, 150, 18, 180);

代码时 , 先后执行 A -> B -> C 类的构造函数 ;

执行

	// 调用 C 的拷贝构造函数
	C c2 = c;

代码时 , 先执行 A -> B 的构造函数 , 然后执行 C 的拷贝构造函数 ;


2、代码示例 - 构造 / 析构 函数调用顺序分析


#include "iostream"
using namespace std;

class A
{
public:
	// 带参构造函数
	A(int age, int height)
	{
		m_age = age;
		m_height = height;
		cout << "执行 A 的构造函数" << endl;
	}

	~A()
	{
		cout << "执行 A 的析构函数" << endl;
	}

public:
	int m_age;		// 年龄
	int m_height;	// 身高
};

class B
{
public:
	// 带参构造函数
	B(int age, int height)
	{
		m_age = age;
		m_height = height;
		cout << "执行 B 的构造函数" << endl;
	}

	~B()
	{
		cout << "执行 B 的析构函数" << endl;
	}

public:
	int m_age;		// 年龄
	int m_height;	// 身高
};

class C
{
public:
	C() : m_age(10), m_b(5, 110), m_a(10, 150), m_const_int(888)
	{}

	// 构造函数中的参数可以作为 参数列表 中的参数值
	C(int age, int ageOfA, int heightOfA, int ageOfB, int heightOfB) 
		: m_age(age),m_b(ageOfB, heightOfB), m_a(ageOfA, heightOfA), m_const_int(888)
	{
		cout << "执行 C 的构造函数" << endl;
	}

	C(const C& c) : m_b(5, 110), m_a(10, 150), m_const_int(888)
	{
		// 上面的 3 个变量 , 必须在初始化列表中初始化 
		// m_age 可以在后续进行单独赋值 , 可以不在初始化列表中进行初始化
		// 由于 A 和 B 都没有默认构造函数 , 必须在初始化列表中调用 有参构造函数
		// m_const_int 成员是常量 , 只能初始化一次 , 不能赋值 , 
		//	因此也必须在初始化列表中进行初始化
		cout << "执行 C 的 拷贝构造函数" << endl;
	}

	~C()
	{
		cout << "执行 C 的析构函数" << endl;
	}
public:
	int m_age;		// 年龄
	A m_a;			// A 类型成员变量
	B m_b;			// B 类型成员变量
	const int m_const_int;	// 常量成员
};


int main()
{
	// 通过 C 的有参构造函数
	// 其中 构造函数中的参数 作为 参数列表 中的参数值
	C c(10, 10, 150, 18, 180);

	// 调用 C 的拷贝构造函数
	C c2 = c;


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
}

执行结果 :

执行 A 的构造函数
执行 B 的构造函数
执行 C 的构造函数
执行 A 的构造函数
执行 B 的构造函数
执行 C 的 拷贝构造函数
Press any key to continue . . .
执行 C 的析构函数
执行 B 的析构函数
执行 A 的析构函数
执行 C 的析构函数
执行 B 的析构函数
执行 A 的析构函数

D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Debug\HelloWorld.exe (进程 19692)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

在这里插入图片描述

更多推荐

B树的插入和删除

1.B树的插入1.核心要求对m阶B树——除根节点外,结点关键字个数[m/2]−1≤n≤m−1[m/2]-1≤n≤m-1[m/2]−1≤n≤m−1子树0<关键字1<子树1<关键字2<子树2<…新元素一定是插入到最底层“终端节点”,用“查找”来确定插入位置.2.具体步骤若插入后结点关键字个数未超过上限,则无需做其他处理.在

PX4 固件常用 QGroundControl 参数设置

一、安全检查1.CBRK_USB_CHK(USB连接检查)检查USB连接飞控,若连接则不允许解锁,默认情况下有USB连接时是无法解锁的,如果需要插USB解锁,需要设置为1978482.CBRK_IO_SAFETY(安全开关检查)检查安全开关,安全开关未打开则不允许解锁,默认情况下需要打开安全开关才能解锁,如果需要禁用安

centos7安装安装python3.11,安装Home Assistant

一,下载并编译安装python3.111、python源码地址:https://www.python.org/ftp/python/3.11.4/Python-3.11.4.tgz2、准备编译环境yumupdate-yyum-ygroupinstall"Developmenttools"yum-yinstallbzip

java基础-并发编程-CountDownLatch(JDK1.8)源码学习

CountDownLatch方法调用与类关系图一、初始化:publicCountDownLatch(intcount)publicCountDownLatch(intcount){if(count<0)thrownewIllegalArgumentException("count<0");this.sync=newSy

LVS负载均衡群集——LVS-NAT模式搭建和LVS-DR模式搭建

目录lvs工作模式1、NAT模式(VS-NAT)2、直接路由模式(VS-DR)3、IP隧道模式(VS-TUN)LVS调度算法LVS群集类型1)负载均衡群集LB2)高可用群集HA3)高性能运输群集HPCLVS-NAT模式搭建1、NFS部署2、web服务器部署(节点服务器)3、负载调度器配置4、使用客户端测试LVS-DR模

pod调度

定向调度通过标签选择器定向调度到node上此调度方式是硬性现在如果匹配不上会调度失败#在node上加标签klabelnodesdev4-workerzone=north#查看kgetnodedev4-worker--show-labels创建pod的yaml文件#通过标签选择器定向调度到指定nodeapiVersion

java运行以jar包的形式运行和tomcat运行的区别和联系?

Java运行以JAR包形式和Tomcat运行之间存在一些区别和联系:区别:部署方式:JAR包形式的Java应用可以作为独立的进程运行,通过命令行或脚本启动。而Tomcat是一个Web服务器和Servlet容器,需要将应用程序打包成WAR文件并部署到Tomcat中。架构:JAR包形式的应用通常是简单的独立应用,将所有的依

web自动化测试 —— cypress测试框架

一、cypress简介基于JavaScript的前端测试工具可以对浏览器中运行的任何内容进行快速、简单、可靠的测试对每一步操作都支持回看覆盖了测试金字塔模型的所有测试类型【界面测试,集成测试,单元测试】底层协议不采用WebDriver>Cypress官网:https://www.cypress.io/二、cypress

随机抽样一致RANSAC

文章目录RANSAC简介RANSAC算法Ransac在3D视觉中的用法直线拟合单应性矩阵拟合RANSAC的优缺点RANSAC的优点RANSAC的缺点RANSAC在弯曲场景中的缺点:RANSAC适用场景RANSAC简介RANSAC是RANdomSAmpleConsensus的缩写,中文翻译叫随机采样一致。它可以从一组观测

嵌入式开发环境Vscode开发STM32单片机程序

STM32单片机非常强大,大多数教程都是使用keil编译器,keil是收费的而gcc是开源免费的。这里介绍一些使用gcc+vscode开发单片机程序的经验。(这里不解释gcc是什么)。​第一:环境准备gccARM开发者官网https://developer.arm.com/我有个习惯:尽量使用免安装版软件,直接解压到软

华为云云耀云服务器L实例评测-搭建基于hexo的个人博客

1、演示访问地址:演示传送门开头先来一个效果图。2、准备服务器前面有介绍了一下华为云云耀云服务器L实例评测以及简单的配置用法,具体可以看上篇的博客。https://blog.csdn.net/yongqing_/article/details/132867889我这里用的是华为云云耀云服务器L实例,2核2G的配置。然后

热文推荐