C++初始化列表详解

news2024/9/20 14:36:45

写在前面:

初始化列表是一种用于初始化成员变量的语法结构,它可以在类的构造函数中使用,用于初始化类的成员变量
列表初始化指的是 是一种初始化变量的简洁方式,可以用花括号{}来表示。列表初始化可以用于各种类型的变量,包括基本类型、数组、结构体、类等。
今天先来浅浅讲一下初始化列表,列表初始化在后面的博客会整理出来

一、常规初始化成员变量方式

话不多说我们先来看看一些初始化成员变量的方式

1.1 默认构造函数

作为类的六大默认成员函数,构造函数主要作用是什么呢?

构造函数是一种用于创建对象初始化对象成员的特殊函数,它在对象创建时被调用,执行一些必要的操作以确保对象的正确性和一致性,每个类都可以定义一个或多个构造函数,用于初始化对象的成员变量。

这里浅浅说一下 初始化和赋值的区别
避免有的同学对其概念混淆,将类成员变量的初始化和 " = " 赋值 混为一谈

1.1.1 初始化

初始化是在创建一个变量时为其赋予一个初始值的过程,可以使用多种方式进行初始化,包括默认初始化、值初始化、直接初始化、列表初始化等。
例如:

int a; // 默认初始化,a的值是未定义的
int b = 0; // 值初始化,b的值为0
int c(1); // 直接初始化,c的值为1
int d{2}; // 列表初始化,d的值为2

1.1.2 赋值

赋值是在变量已经存在的情况下,将一个新的值赋给该变量的过程。赋值使用赋值操作符"="来完成,例如:

int a = 1;
a = 2; // 将2赋值给变量a

继续回到我们的成员初始化
例如我们使用默认的构造函数来对类的成员变量初始化

class Test
{
public:
	Test()//默认构造函数,写不写都可以
	{

	}
	void Print()
	{
		cout << m_ia << " " << m_ib << endl;
	}
	~Test()
	{

	}
private:
	int m_ia;
	int m_ib;
};
int main()
{
	Test clsT1;
	clsT1.Print();
	return 0;
}

运行结果:
在这里插入图片描述
可以看到,使用默认的构造函数,只是创建了这个变量,没有赋初值,因此是一个默认值,当然我们也可以在构造函数中对其赋初值,示例:
在这里插入图片描述
这是不带参的构造函数,正如上面提到的,每个类可以定义一到多个构造函数,因此我们也可以定义一个带参的构造函数:

1.2 带参的构造函数

代码示例:

#include<iostream>
using namespace std;
class Test
{
public:
	Test(int a, int b)
	{
		m_ia = a;
		m_ib = b;
	}
	void Print()
	{
		cout << m_ia << " " << m_ib << endl;
	}
	~Test()
	{

	}
private:
	int m_ia;
	int m_ib;
};
int main()
{
	Test clsT2(1, 2);
	clsT2.Print();
	return 0;
}

结果:
在这里插入图片描述
这种方式也是OK 的,接下来我们再看这样一种情况

//当我们定义下面这两种成员变量,一个是 const 类型的成员变量
private:
	const int m_ia;

如果再使用我们原来的构造函数时:
在这里插入图片描述
这时候发现编译器开始给我们画红线了,有的小伙伴可能和我一样有疑问,没错啊 const 类型的成员变量本就不可以被修改,那么这样的变量定义再类里面有什么意义呢?

1.保护数据:常量成员变量可以在类中定义不可变的数据,保护数据的一致性和完整性,避免意外的数据修改。这对于提高代码的可靠性和安全性非常重要。
2.提高效率:常量成员变量可以在对象创建时进行初始化,避免了因为对象初始化后再进行赋值操作而浪费时间和资源。
3.优化代码:常量成员变量可以使代码更加清晰和易于理解,因为它们的值是固定的,不会被修改。这样可以使代码更加简洁和易于维护。
4.精细控制权限:常量成员变量可以被声明为private访问权限,这样只有类的成员函数可以访问它们,确保了其它代码无法修改该数据。

那么常成员变量如何进行初始化呢?接下来就可以把我们今天的主题,初始化列表 拿出来讲了

二、初始化列表

我们先来看看初始化列表的方式:
代码示例:

#include<iostream>
using namespace std;
class Test
{
public:
	//初始化列表的方式
	Test():m_ia(0),m_ib(1)
	{
		
	}
	void Print()
	{
		cout << m_ia << " " << m_ib << endl;
	}
	~Test()
	{

	}
private:
	int m_ia;
	int m_ib;
};
int main()
{
	Test clsT2;
	clsT2.Print();
	return 0;
}

结果:
在这里插入图片描述
可以看到初始化列表初始化成员变量的方式还是和常规的构造函数不一样的,初始化的代码是写在 构造函数的 () 后的

构造函数() 再接着一个冒号 " : "  m_a(value) 
再接着用逗号将成员变量一一隔开 " , " m_b(value2) , m_n(value)
最后一个成员变量初始化完后不用写逗号 直接跟一个 {} 即可

当然如果成员变量很多的话,一行写不下可以分行来写,例如:

#include<iostream>
#include<string>
using namespace std;
class Test
{
public:
	Test():
		m_ia(0),
		m_ib(1),
		m_str1("hello"),
		m_d1(3.14),
		m_c1('a')
	{
		
	}
	void Print()
	{
		cout << m_ia << " " << m_ib << " "<< m_str1<<" "<< m_d1 << " "<< m_c1<<endl;
	}
	~Test(){}
private:
	int m_ia;
	int m_ib;
	string m_str1;
	double m_d1;
	char m_c1;
};
int main()
{
	Test clsT2;
	clsT2.Print();
	return 0;
}

看下结果:
在这里插入图片描述

2.1 使用初始化列表初始化成员变量的顺序可以改变吗?

答案:尽量按照变量声明的顺序来进行初始化
我们先来看一下代码示例:

#include<iostream>
#include<string>
using namespace std;
class Test
{
public:
	Test():m_ic(1), m_ia(2),m_ib(3)
	{
		
	}
	void Print()
	{
		cout << m_ia << " " <<m_ib<<" "<< m_ic<<endl;
	}
	~Test(){}
private:
	int m_ia;
	int m_ib;
	int m_ic;
};
int main()
{
	Test clsT2;
	clsT2.Print();
	return 0;
}

运行结果:
在这里插入图片描述
没啥问题,我们再试试:
使用 m_ia +1 来初始化 m_ic
在这里插入图片描述
也没啥问题,再试试用 m_ic + 1来初始化 m_ib
在这里插入图片描述
由于声明顺序如下:

1	int m_ia;
2	int m_ib;
3	int m_ic;

当我们成员变量初始化依赖其他的成员变量时,最好按照按照声明的顺序来一一对其进行初始化,且依赖关系也要按照声明的顺序来控制,否则容易造成未知的一些风险

2.2 那些情况下必须要使用初始化列表?

像上述的那些成员变量可以用常规的构造函数初始化,也可以用初始化列表进行初始化,比较简洁,而以下这些情况必须要用初始化列表来初始化

2.2.1 const 成员变量

代码示例:

class Test
{
public:
	Test()
	{
		m_ia = 2;//报错表达式必须是可修改的左值
	}
	Test():m_ia(2) //初始化列表初始化
	{
		
	}
	void Print()
	{
		cout << m_ia <<endl;
	}
	~Test(){}
private:
	const int m_ia;
};

在这里插入图片描述
当为常成员变量时只能用初始化列表来进行初始化
在这里插入图片描述

2.2.2 引用成员变量

引用和指针的区别大家应该都知道
引用就是 别名 ,例如张三别名有 小张、小张三,小张和小张三都代表 张三本人
而 指针就是有个人他认知张三,可以通过这个人来找到张三
而引用有一个特点就是必须要在定义时就初始化
示例:
在这里插入图片描述
因此引用成员变量必须使用初始化列表进行初始化
代码示例:

#include<iostream>
#include<string>
using namespace std;
class Test
{
public:
	Test(int&a):m_ia(a) //引用成员变量使用初始化列表初始化
	{
		
	}
	void Print()
	{
		cout << m_ia <<endl;
	}
	~Test(){}
private:
	int& m_ia;
};
int main()
{
	int a = 9;
	Test clsT2(a);
	clsT2.Print();
	return 0;
}

2.2.3 基类初始化

我们都知道,子类继承自父类时,实例化出子类对象时需要先调用父类的构造函数,再调用子类自己的构造函数 我们看一下这一段示例代码:

#include<iostream>
#include<string>
using namespace std;
class Base
{
	Base(string str)
	{}
private:
	string m_strName;
};
class Test : public Base
{
public:
	Test(int&a):m_ia(a)
	{
		
	}
	void Print()
	{
		cout << m_ia <<endl;
	}
	~Test(){}
private:
	int& m_ia;
};
int main()
{
	int a = 9;
	Test clsT2(a);
	clsT2.Print();
	return 0;
}

来看一下编译结果:
在这里插入图片描述
类没有定义自己的默认构造函数,而是定义了带参的构造函数,那么这时候必须使用初始化列表来对基类进行初始化
这里说一下:只有类没有任何构造函数时,编译器才会自动生成默认的构造函数
在这里插入图片描述

2.2.4 初始化成员变量为类对象

当一个类的成员变量是一个类对象时,那么就必须使用初始化列表来对其进行初始化,例如:

class Apple
{
public:
	Apple(string str)//没有默认构造函数
	{
		m_strName = str;
	}
private:
	string m_strName;
};
class Test 
{
public:
	Test()//报错 Apple 不存在默认的构造函数
	{

	}
	~Test(){}
private:
	int m_ia;
	Apple a1; //类对象
};

在这里插入图片描述
这时候我们就需要使用初始化列表来对其进行初始化
在这里插入图片描述

三 、初始化列表的优缺点有哪些

3.1 优点:

优点:
1.效率高:使用初始化列表初始化成员变量可以避免在构造函数体中进行多余的赋值操作,从而提高代码的执行效率。
2.精细控制:使用初始化列表可以在构造函数中精细控制成员变量的初始化顺序,以及对于常量成员变量和引用成员变量的初始化。
3.可读性强:使用初始化列表可以使代码更加简洁和易于阅读,因为它们将类的成员变量的初始化放在了一个地方,使代码更加清晰。
4.语法简单:初始化列表的语法简单明了,可以很容易地理解和使用。

3.2 缺点:

1.初始化顺序:如果不按照成员变量在类中的声明顺序,按照不同的顺序对成员变量进行初始化,可能会导致一些难以预测的问题。
2.可读性差:当需要初始化的成员变量很多时,初始化列表可能会变得冗长和难以阅读,影响代码的可读性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/660182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

某农业大学c/c++第五次实验(类和对象)

1.Time时间类 【问题描述】 设计一个Time类&#xff0c;并设计多个重载的构造函数&#xff0c;可以设置时间、进行时间的加减运算、按12小时格式和24小时格式输出时间。 例如&#xff1a; 其中构造函数Time::Time(int s)根据总秒数计算hour、minute、second并构造对象&#x…

C语言,BMP格式解析,生成不同位深的图片。

0.前言 目录 0.前言 1.BMP格式详解 1.1图片的构成 1.2BMP格式 1.2.1文件头 1.2.2位图信息头 1.2.3调色板 1.2.4位图数据 2.生成 3.总结 最近工作任务繁重且对我来说小有难度&#xff0c;但是没有困难的事情做起来怎么有收获呢&#xff1f; 今天推荐一本书《寻找光…

Windows10环境下载安装Oracle19c教程

Windows10环境下载安装Oracle19c教程 前言1. 官网下载2. 运行setup.exe2.1 解压缩2.2 报错解决 3.正式安装3.1 配置选项3.2 系统类3.3 oracle主目录用户3.4 安装位置3.5 先决条件检查3.6 安装产品3.7 安装完成3.8 完全卸载oracle(非必备) 4. 安装验证4.1 CMD验证4.2 SQL Plus4.…

本周大新闻|Quest游戏订阅服务曝光;平价版苹果XR头显25年推出

本周XR大新闻&#xff0c;AR方面&#xff0c;彭博苹果预计25年推出更亲民头显&#xff1b;Vision Pro外屏含微透镜&#xff1b;WWDC23苹果高管脱口秀爆料更多细节&#xff1b;外媒爆料称Vision Pro预期销量改为15万台&#xff1b;VitreaLab展示RGB激光背光技术。还是提前说一下…

leetcode刷题之字符串相关问题

344.反转字符串 方法一&#xff1a;找中间结点&#xff0c;头尾翻转 var reverseString function(s) {let len s.lengthlet mid Math.floor((s.length - 1) / 2) //向下取整 如果长度是奇数&#xff0c;那么mid是最中间的结点 如果长度是偶数&#xff0c;那么mid是中间两个…

【Java技术专题】「攻破技术盲区」带你攻破你很可能存在的Java技术盲点之动态性技术原理指南(反射技术专题)

带你攻破你很可能存在的Java技术盲点之动态性技术原理指南 带你攻破你很可能存在的Java技术盲点之动态性技术原理指南编程语言的类型静态类型语言动态类型语言 技术核心方向反射API反射案例介绍反射功能操作获取构造器长度可变的参数 - 构造方法使用反射 API 获取参数长度可变的…

【软件测试】

系列文章目录 文章目录 系列文章目录前言第四章 单元测试4.1 软件测试过程概述4.2 什么是单元测试4.2.1 单元测试的定义4.2.2 单元测试的重要性4.2.3 单元测试原则 4.3 单元测试的目标和任务4.3.1 单元测试的目标&#xff1a;单元模块被正确编码4.3.2 单元测试的主要任务 4.4 单…

FreeRTOS:事件标志组

目录 一、事件标志组简介1.1事件位(事件标志)1.2事件组1.3事件标志组和事件位的数据类型 二、创建事件标志组2.1函数 xEventGroupCreate()2.2函数xEventGroupCreateStatic() 三、设置事件位3.1函数 xEventGroupClearBits()3.2函数xEventGroupClearBitsFromISR()3.3函数 xEventG…

Python模块MarkupPy 自定义html报告

简介 MarkupPy是Python模块用于生成HTML和XML格式的字符串。它的主要作用是提供了一种比原生HTML/XML更加易读和易写的编写方式&#xff0c;通过Python代码来生成HTML或XML代码。 使用MarkupPy&#xff0c;可以在Python中使用不同的对象类型和方法&#xff0c;来动态地生成HTML…

做自动化测试老是失败?你真的会做吗?资深测试的总结整理...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化正在不断普…

揭开生成式人工智能的力量:60+医疗保健应用场景

预计生成式AI在医疗保健领域的增长速度将超过任何其他行业。在医疗技术领域&#xff0c;AI可带来更高效流程、个性化客户互动、更大的创新和更高价值。为了帮助领导者理解这些机会&#xff0c;BCG最近研究了医疗技术中生成式AI的60多个应用场景&#xff1a;从产研和软件开发到业…

Android自动化测试,5个必备的测试框架

Appium Appium是一个开源的移动测试工具&#xff0c;支持iOS和Android&#xff0c;它可以用来测试任何类型的移动应用&#xff08;原生、网络和混合&#xff09;。作为一个跨平台的工具&#xff0c;你可以在不同的平台上运行相同的测试。为了实现跨平台的功能&#xff0c;Appi…

关于数据库索引的入门简述

一、简介 数据库索引是现代数据库中高效数据检索的一个重要工具。它在优化查询性能和加快数据检索操作方面发挥着重要作用。这里我们深入了解下数据库索引其内部工作原理、优点和局限性。 二、数据库 1、SQL 数据库 为了理解索引&#xff0c;先说一句数据库&#xff0c;数据库…

Jenkins小技巧汇总

设置变量 设置全局环境变量 全局变量除了系统内置的全局环境变量之外&#xff0c;用户也可以设置全局变量。设置路径&#xff1a;【Dashboard】–>【Manage Jenkins】–>【System Configuration 下的 System】–>【Global properties】从描述中我们可以看到&#xf…

Jetson nano 之 ROS入门 - - 深度学习环境配置

文章目录 前言一、Anaconda安装二、Pytorch 与 TensorFlow 环境配置三、TensorRT 推理引擎配置总结 前言 Jetson Nano是一款由NVIDIA推出的小型计算机&#xff0c;其性能优异、功耗低、体积小巧&#xff0c;非常适合用于嵌入式系统和边缘设备的深度学习应用。Jetson Nano搭载了…

【Git原理与使用】-- 基本操作

目录 添加文件 查看objects中的文件 小结 修改文件 版本回退 回退的回退 小结 撤销修改 情况一&#xff1a;对于工作区的代码&#xff0c;还没有 add 情况二&#xff1a;已经 add &#xff0c;但没有 commit 情况三&#xff1a;已经 add &#xff0c;并且也 commit …

Cordic IP核使用说明以及避坑记录

Cordic IP核使用说明以及避坑记录 参考文章&#xff1a;(140条消息) Vivado cordic IP核rotate和translate使用详解(附有代码)_cordic ip核 rotate_迎风打盹儿的博客-CSDN博客 (140条消息) VIVADO cordic IP核_卡布奇诺加勺糖的博客-CSDN博客 文章目录 Cordic IP核使用说明以及…

面试题:推排序是一种稳定排序吗?

面试题&#xff1a;推排序是一种稳定排序吗&#xff1f; 在回答该问题前&#xff0c;首先需要了解什么是稳定排序。 稳定性就是指对于两个关键字相等的记录&#xff0c;它们在序列中的相对位置&#xff0c;在排序之前和排序之后没有发生改变。通俗地讲就是有两个关键字相等的…

Node.js---菜鸟教程

文章目录 创建第一个应用创建 Node.js 应用 NPM 使用介绍使用 npm 命令安装模块本地安装使用 package.json模块的操作 回调函数阻塞代码实例非阻塞代码 事件循环事件驱动程序 EventEmitterEventEmitter 类方法实例error 事件继承 EventEmitter Buffer&#xff08;缓冲区&#x…

Redis-缓存

新增或者更新数据时,创建以后顺便存到redis中去【维护缓存】 获取的时候先从redis缓存中拿数据 如果拿数据的时候为空,则到数据库中拿数据,后再存到redis缓存中去 大量的商品【包括冷门商品】都进行上面的缓存,那么就很耗内存 针对每个数据进行缓存的时候 维护一个过期时间…