【C++、C++11】列表初始化、右值引用

news2025/1/31 11:05:03

文章目录

  • 📖 前言
  • 1. 统一的列表初始化
    • 1.1 { } 花括号初始化:
    • 1.2 std::initializer_list:
  • 2. 右值引用
    • 2.1 什么是左值和右值:
    • 2.2 右值的分类:
    • 2.3 左值引用和右值引用的比较
    • 2.3 右值的使用场景:
    • 2.4 新的类功能:

📖 前言

在一开始学C++之前我们就简单的了解了一下C++的发展历史,重要的几个结点如下:

阶段内容
C withclasses类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等
C++98C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美国标准化协会认可,以模板方式重写C++标准库,引入了STL(标准模板库)
C++11增加了许多特性,使得C++更像一种新语言,比如:正则表达式、基于范围for循环、auto关键字、新容器、列表初始化、标准线程库等
C++20自C++11以来最大的发行版,引入了许多新的特性,比如:**模块(Modules)、协程(Coroutines)、范围(Ranges)、概念(Constraints)**等重大特性,还有对已有特性的更新:比如Lambda支持模板、范围for支持初始化等

当然在这些之中还发行了其他的版本,C++还在不断的向后发展。但是:现在公司主流使用还是 C++98和C++11

  • 相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。
  • 相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个重点去学习。

C++11官网:👉 传送门


1. 统一的列表初始化

1.1 { } 花括号初始化:

在我们之前学的C++98中,我们初始化一个变量或者是一个数组或一个对象可以是:

struct Point
{
	Point(int x = 1, int y = 2)
		: _x(x)
		, _y(y)
	{}

	int _x;
	int _y;
};

int main()
{
	int x1 = 1;
	int x2 = int();
	
	int* p1 = new int(1);
	int* p2 = new int[3]{ int(1),int(3),int(4) };
	int* p3 = new int[3]{ 1,3,4 };
	
	Point p(1, 2);
	Point();
	
	return 0;
}

现在在C++11中我们可以按照如下的方式初始化:

int main()
{
	int x1 = { 2 };
	int x2{ 3 };

	int array1[]{ 1, 2, 3, 4, 5 };
	int array2[5]{ 0 };

	Point p{ 1, 2 };

	return 0;
}

上面支持,本质就更好支持new[]的初始化问题:

C++98只能new单个对象,new多个对象没办法很好的初始化了,定义一个对象数组是很不方便的,至少应该如下定义:

int main()
{
	Point p1, p2, p3, p4;
	Point* pp1 = new Point[]{ p1, p2, p3, p4 };
	Point* pp2 = new Point[]{ Point(1, 1), Point(2, 2), Point(3, 3), Point(4, 4) };

	return 0;
}

而在C++11中我们直接可以:

int main()
{
	Point p1[] = { {1, 1}, {2, 2}, {3, 3}, {4, 4} };
	Point p2[]{ {1, 1}, {2, 2}, {3, 3}, {4, 4} };
	Point* p3 = new Point[]{ {1, 1}, {2, 2}, {3, 3}, {4, 4} };

	return 0;
}

类比C++98的隐式类型转换:

C++98中我们知道,单参数的构造函数可以直接给个值直接构造,C++11可以说是对C++98这一特性进行了延伸:

  • 单参数构造函数隐式类型转换复习:👉 传送门
class Date
{
public:
	//explicit Date(int year, int month, int day)
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1{ 2023, 3, 7 };
	Date d2 = { 2023, 3, 7 };

	return 0;
}
  • 可以理解成C++98是单参数隐式类型转换,C++11是多参数隐式类型转换(同时支持了把=去掉)
  • 同样的类似于C++98,如果不想发生隐式类型转化,加上explicit

在这里插入图片描述

1.2 std::initializer_list:

std::initializer_list的介绍文档:
在这里插入图片描述
std::initializer_list就像是一个容器一样,我们先来看一下其类型:

在这里插入图片描述
底层大概的样子:

  • 是只能读不能写的
  • 可以认为在某个区域开了一块空间,将花括号中的东西存起来
  • 可以认为是常量的数组把它支持起来了

在这里插入图片描述

  • std::initializer_list支持迭代器
  • 注意:
    • std::initializer_list内容是不能被改的

为什么突然将到一个容器了呢?

  • 首先std::initializer_list是C++11新提出来的
  • 其次有了std::initializer_list,之前学的容器也都支持了用{ }列表初始化

在这里插入图片描述
以vector,和map为例,C++11之后就我们之前学的容器可以直接通过{ } 列表初始化了:

int main()
{
	vector<Date> v1 = { { 2023, 3, 7 }, { 2023, 3, 7 }, { 2023, 3, 7 } };
	vector<Date> v2{ { 2023, 3, 7 }, { 2023, 3, 7 }, { 2023, 3, 7 } };

	map<string, string> dict1 = { { "string", "字符串" }, { "sort", "排序" } };
	map<string, string> dict2{ { "string", "字符串" }, { "sort", "排序" } };

	return 0;
}

先构造一个initializer_list,再用initializer_list构造一个vector,具体过程:

在这里插入图片描述
也可以和之前隐式类型转换联系起来,也是中间产生了一个临时对象(initializer_list),再用临时对象去拷贝构造。


2. 右值引用

2.1 什么是左值和右值:

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。

什么是左值,什么是左值引用:

  • 左值是一个表示数据的表达式,如:变量名或解引用的指针
  • 我们可以获取它的地址 + 可以对它赋值
  • 左值可以出现赋值符号的左边,右值不能出现在赋值符号左边
  • 定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址
  • 左值引用就是给左值的引用,给左值取别名

什么是右值,什么是右值引用:

  • 右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等
  • 右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址
  • 一个左值经过move之后就可以变成一个右值
  • 右值引用就是对右值的引用,给右值取别名

总结:

不能说出现在赋值符号左边的就叫左值,在赋值符号右边的也有可能是左值(int a = 1; int b = a) a 可以赋值和取地址,右值却不能出现在赋值符号的左边,const修饰的对象也叫左值(特例),左值一定可以取地址,但不一定能赋值,右值不能出现在赋值符号左边,右值不能取地址。

int main()
{
	//左值
	int a = 1;
	int& b = a;

	//右值
	double x = 1.1, y = 2.2;

	//以下几个都是常见的右值
	10;
	x + y;
	fmin(x, y);

	//以下几个都是对右值的右值引用
	int&& rr1 = 10;
	double&& rr2 = x + y;
	double&& rr3 = fmin(x, y);

	//这里编译会报错:error C2106: “=”: 左操作数必须为左值
	10 = 1;
	x + y = 1;
	fmin(x, y) = 1;

	return 0;
}

注意:

  • 需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址
  • 也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1
  • 如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感觉很神奇
  • 这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要

2.2 右值的分类:

上述我们讲到右值不能放在 = 符号的左边,即不能赋值,我们可以理解成其是一个临时对象,具有const属性的一个临时对象,我们对其进行详细的分类,分成两类:

  • 纯右值:
  • 10、a + b…
  • 将亡值:
  • 匿名对象string(“222”)、to_string(1234)、自定义的对象、move(s1)…

2.3 左值引用和右值引用的比较

左值引用:

  • 左值引用只能引用左值,不能引用右值
  • 但是const左值引用既可引用左值,也可引用右值

右值引用:

  • 右值引用只能右值,不能引用左值
  • 但是右值引用可以引用move以后的左值

注意:

  • 我们可以理解成右值是临时对象,具有const属性不能被修改的值
  • 所以我们可以用const类型的引用来接收一个右值
  • 我们之前用const类型的引用来接收一个左值是因为权限是可以缩小的,但是权限不能放大

2.3 右值的使用场景:

在我们之前的C++学习中,我们学的都是左值引用,左值引用可以提高程序的效率,特别是函数传引用返回的时候。

左值引用复习:👉 传送门

左值引用的短板:

  • 虽然左值引用在函数中返回一个引用可以提高效率,但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回
  • 此时就只能传值返回,例如:string中的 to_string(int value)函数中可以看到,这里只能使用传值回,传值返回会导致至少1次拷贝构造,编译器会优化(如果是一些旧一点的编译器可能是两次拷贝构造)

在这里插入图片描述
很显然to_string是要将数字转换成字符串然后返回,那么这个返回的字符串就是个临时对象,除了函数作用域就销毁了,传引用返回就拿不到了,那块空间已经被销毁了,所以这里只能传值返回。
在这里插入图片描述
很显然即使是编译器优化了之后,也是至少有一次拷贝。

众所周知把大象放到冰箱里一共有三步:

  • 第一步:打开冰箱门
  • 第二步:把大象放进去
  • 第三步:关上冰箱门

那么将大象从一号冰箱放到二号冰箱一共有几步:

  • 二臂方法: 在二号冰箱里复制一份一模一样的大象,再将一号冰箱里的大象干掉
    • C++98拷贝方法:函数的传值返回就是按照这种方法,这还是编译器优化之后的样子,优化之前中间还有个临时变量,并且是深拷贝返回,效率更低下!
  • 聪明办法:直接把冰箱的一号和二号的编号调换一下,这样就实现了大象在二号冰箱里了
    • C++11右值引用方法:当函数传一个将亡值(右值)返回的时候,C++11提供了一个移动构造,直接用另一个新的对象的指针来接管了原来的对象。

右值引用和移动语义解决上述问题:

  • 在string中增加移动构造,移动构造本质是将参数右值的资源窃取过来
  • 占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己

在这里插入图片描述
C++11中编译器会直接将传值返回识别成一个右值,然后调用移动构造:

在这里插入图片描述
在我们之前没有移动构造的时候,我们调用的是拷贝构造,之前我们提到过const类型既可以接收右值,也可以接收左值,所以之前我们是能匹配的上的,但是现在现在有了移动构造,编译器会匹配最匹配的那一个构造函数。

将一个左值move之后就成了右值,右值的资源有可能会被转移:
在这里插入图片描述
在这里插入图片描述


2.4 新的类功能:

默认成员函数:

  • 原来C++类中,有6个默认成员函数:
    • 构造函数
    • 析构函数
    • 拷贝构造函数
    • 拷贝赋值重载
    • 取地址重载
    • const 取地址重载

C++11 新增了两个默认成员函数:

  • 移动构造函数和移动赋值运算符重载
  • 移动构造和移动赋值是有条件的,并且默认生成的达到不了我们想要的效果
  • 所以一般我们自己实现

要求:

  • 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

STL的容器,C++11以后,都提供移动构造和移动赋值,右值引用 + 移动构造补齐了C+ +传参和传返回值的最后一块短板。

STL容器中的各种插入也用到了右值引用:==

当这些容器的元素是某个对象的时候,插入的话要new一个新的元素,也会产生深拷贝的问题,所以这里用到右值引用将会非常方便:

在这里插入图片描述

int main()
{
	list<YY::string> lt;
	YY::string s1("1111");
	//这里调用的是拷贝构造
	lt.push_back(s1);            //->void push_back (const value_type& val);

//如果只是为了插入,下面的效果明显更好

	//下面调用都是移动构造
	lt.push_back("2222");
	lt.push_back(std::move(s1)); //->void push_back (value_type&& val);

	YY::string s2("hello world");
	//YY::string s3 = std::move(s2);
	YY::string s4 = s2;

	return 0;
}

在这里插入图片描述
注意:

  • 不要随意的将左值move成右值,不然会造成资源的丢失!!

在这里插入图片描述

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

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

相关文章

SpringBoot整合Quartz以及异步调用

文章目录前言一、异步方法调用1、导入依赖2、创建异步执行任务线程池3、创建业务层接口和实现类4、创建业务层接口和实现类二、测试定时任务1.导入依赖2.编写测试类&#xff0c;开启扫描定时任务3.测试三、实现定时发送邮件案例1.邮箱开启IMAP服务2.导入依赖3.导入EmailUtil4.编…

为「IT女神勋章」而战

大家好&#xff0c;我是空空star&#xff0c;今天为「IT女神勋章」而战 文章目录前言一、IT女神勋章二、绘制爱心1.htmlcssjs来源&#xff1a;一行代码代码效果2.python来源&#xff1a;C知道代码效果3.go来源&#xff1a;复制代码片代码效果4.java来源&#xff1a;download代码…

游戏算法-游戏AI状态机,python实现

AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计&#xff0c;使得游戏内的单位能够感知周围环境&#xff0c;并做出相应的动作表现的技术。游戏AI作为游戏玩法的一大补充&#xff0c;在各种游戏中都有广泛的应用&#xff0c;比如可以和玩家交互聊天的NPC&#…

用户体验设计—影响定制化设计的因素

0 前言最近在上信息构建这门课&#xff08;名为信息构建&#xff0c;但感觉叫用户体验设计更好。。。&#xff09;老师是研究信息行为、人智交互这块的&#xff0c;所以实验课要求我们先学习一个实际的设计案例&#xff0c;让我们搞懂影响定制化设计的因素。所以这篇文章讲讲我…

七色电子标签

机种名 电子会议桌牌 型号 ESL_7color_7.3_D 外观尺寸 176.2x137.15x80mm 产品重量 268g 可视区域 163.297.92mm 外观颜色 银色 供电方式 锂电池供电2300mAh&#xff08;Type-C 接口可充电&#xff09; 显示技术 E-INK电子纸&#xff0c;双屏 像素 800x480 像…

ByteTrack: Multi-Object Tracking by Associating Every Detection Box 论文详细解读

ByteTrack: Multi-Object Tracking by Associating Every Detection Box 论文详细解读 文章目录ByteTrack: Multi-Object Tracking by Associating Every Detection Box 论文详细解读ByteTrackByteTrack算法简介ByteTrack算法流程ByteTrack算法描述一&#xff1a;对检测框进行分…

SOA架构的理解

1. SOA概述 SOA&#xff08;Service-Oriented Architecture&#xff0c;面向服务的架构&#xff09;是一种在计算机环境中设计、开发、部署和管理离散模型的方法。SOA不是一种新鲜事物&#xff0c;它是在企业内部IT系统重复构建以及效率低下的背景下提出的。在SOA模型中&#x…

Nexus 3 清理docker镜像

该文章提供了一种清理nexus3中存储的docker镜像的一种新思路 查看docker repo 比如你的docker repo名字叫做test-repo&#xff0c;然后在nexus3首页的seatch下面找到docker&#xff0c;点进去随便查看一个已经上传的镜像 记住上面的Name选项&#xff0c;之后要用到 设定清理…

centos7 oracle19c安装||新建用户|| ORA-01012: not logged on

总共分三步 1.下载安装包:里面有一份详细的安装教程 链接&#xff1a;https://pan.baidu.com/s/1Of2a72pNLZ-DDIWKrTQfLw?pwd8NAx 提取码&#xff1a;8NAx 2.安装后,执行初始化:时间较长 /etc/init.d/oracledb_ORCLCDB-19c configure 3.配置环境变量,不配置环境变量,sq…

【Linux快速入门】文件目录操作

文章目录概念1. Linux文件系统概述2. Linux文件目录结构3. Linux文件和目录操作3.1 文件操作3.1.1 创建文件3.1.2 复制文件3.1.3 移动文件3.1.4 删除文件3.1.5 查看文件3.1.6 输出指令3.1.7 >和>>指令3.2 目录操作3.2.1 创建目录3.2.2 复制目录3.2.3 移动目录3.2.4 删…

Lesson 8.3 ID3、C4.5 决策树的建模流程 Lesson 8.4 CART 回归树的建模流程与 sklearn 参数详解

文章目录一、ID3 决策树的基本建模流程二、C4.5 决策树的基本建模流程1. 信息值&#xff08;information value&#xff09;2. C4.5 的连续变量处理方法三、CART 回归树的基本建模流程1. 数据准备2. 生成备选规则3. 挑选规则4. 进行多轮迭代5. 回归树的预测过程四、CART 回归树…

关于推荐系统的详细介绍

简介推荐系统是一种信息过滤系统&#xff0c;能够自动预测用户对特定产品或服务的偏好&#xff0c;并向其提供个性化的推荐。它通常基于用户的历史行为、个人喜好、兴趣和偏好等&#xff0c;通过数据挖掘和机器学习算法&#xff0c;在大数据的支持下生成个性化的推荐内容&#…

智云通CRM:与权力者沟通的策略有哪些?

权力者通常具备两个特点&#xff1a;忙和目标导向 1.忙 权力者都很忙&#xff08;不忙也会装出很忙的样子&#xff09;&#xff0c;时间精力有限&#xff0c;销售人员眼里的大项目在权力者看来很有可能只是他诸多工作中的一项。因此&#xff0c;即使有不满者的引荐&#xff0c;…

ChatGPT露馅了,它明明就是人

让人工智能理解句子成分和语义&#xff0c;这看起来是件不可能的事&#xff0c;看过流浪地球的都知道&#xff0c;那里面的人工智能哪怕发展到2057年&#xff0c;也听不懂比喻和反问。 那最近大火的chatGPT能不能听懂冷笑话呢&#xff1f;它不仅能写代码、论文&#xff0c;居然…

Spring学习——拦截器

拦截器概念 拦截器&#xff08;Interceptor )是一种动态拦截方法调用的机制&#xff0c;在SpringMVC中动态拦截控制器方法的执行作用: 在指定的方法调用前后执行预先设定的代码阻止原始方法的执行 拦截器与过滤器区别 归属不同&#xff1a;Filter属于Servlet技术&#xff0…

[oeasy]python0101_尾声_PC_wintel_8080_诸神的黄昏_arm_riscv

尾声 回忆上次内容 回顾了 ibm 使用开放架构 用 pc兼容机 战胜了 dec 小型机apple 个人电脑 触击牺牲打 也破掉了 自己 软硬一体全自主的 金身 借助了 各种 软硬件厂商的 力量 最终完成了 pc架构上 的 大一统 操作系统层面 IBM 计划让 msdos和cp/m 分庭抗礼为什么 最后微软…

NC xml配置文件不能生产java文件

在NC开发过程中&#xff0c;新增、或修改了xml文件&#xff0c;在开发工具eclipse中生成或重新生成Java文件&#xff0c;发现生成不了相对应的Java文件。如下图&#xff0c;选中xml文件后&#xff0c;右键点击SpringXml to Java 这种情况其实一般都是xml配置文件有问题&#…

敏捷项目管理的概念,以及与传统项目管理的区别

较之瀑布等传统项目管理模式&#xff0c;敏捷是“适应性的”&#xff0c;而非“预设性的”。团队采用敏捷项目管理可以提高交付速度、协作效率、以及响应市场变化的能力。在这里向大家详细介绍敏捷项目管理的定义、与传统项目管理的区别&#xff0c;以及一些主流的敏捷项目框架…

下一代ERP系统是什么样的呢?什么是智能化ERP系统?AI能改变ERP系统吗?

下一代ERP系统是什么样的呢&#xff1f;什么是智能化ERP系统&#xff1f;AI能改变ERP系统吗&#xff1f;导读1. 用户体验&#xff1a;2. 作业、分析和智能一体化2.1 ERP之采购管理&#xff1a;2.2 ERP之零售商品管理&#xff1a;2.3 ERP之会计和财务管理3. 系统处理大数据导读 …

嵌入式Linux从入门到精通之第十六节:U-boot分析

简介 u-boot最初是由PPCBoot发展而来的,可以引导多种操作系统、支持多种架构的CPU,它对PowerPC系列处理器的支持最为完善,而操作系统则对Linux系统的支持最好目前已成为Armboot和PPCboot的替代品。 特点: 主要支持操作系统:Linux、NetBSD、 VxWorks、QNX、RTEMS、ARTOS、L…