【C++、C++11】列表初始化、右值引用
文章目录
- 📖 前言
- 1. 统一的列表初始化
- 1.1 { } 花括号初始化:
- 1.2 std::initializer_list:
- 2. 右值引用
- 2.1 什么是左值和右值:
- 2.2 右值的分类:
- 2.3 左值引用和右值引用的比较
- 2.3 右值的使用场景:
- 2.4 新的类功能:
📖 前言
在一开始学C++之前我们就简单的了解了一下C++的发展历史,重要的几个结点如下:
阶段 | 内容 |
---|---|
C withclasses | 类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等 |
C++98 | C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(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
提供了一个移动构造,直接用另一个新的对象的指针来接管了原来的对象。
- 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成右值,不然会造成资源的丢失!!
相关文章:

【C++、C++11】列表初始化、右值引用
文章目录📖 前言1. 统一的列表初始化1.1 { } 花括号初始化:1.2 std::initializer_list:2. 右值引用2.1 什么是左值和右值:2.2 右值的分类:2.3 左值引用和右值引用的比较2.3 右值的使用场景:2.4 新的类功能&…...

CMU15-445 Project.3总结
在线测试 Project #3 - Query Execution 以下是Project #3的网址,2022FALL的Project #3是实现一个查询执行,实现一系列算子,用于实现数据库内的SQL计算。项目中的 Query Execution 主要分为三个任务: Access Method Executors…...

002+limou+HTML——(2)HTML文档
000、前言 一般来说一个静态网页拥有四种元素:文字、图片、超链接、音频和视频(注意,即使在web网页中植入Javascript语言,也不一定是动态网页,真正的动态网页判断标准:是否和服务器产生交互) …...

红外传感器模块与 Arduino 连接
红外传感器模块与 Arduino 连接 原文地址 Arduino 红外传感器接口 红外**接近传感器或红外传感器它发射红外光以感知周围环境,并可用于检测物体的运动。由于这是一个无源传感器,它只能测量红外辐射。如果您曾经尝试过设计避障机器人或任何其他基于接近…...

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

华为OD机试 - 五键键盘(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:五键键盘…...

Kubernetes Service简介
Service 之前我们了解了Pod的基本用法,我们也了解到Pod的生命是有限的,死亡过后不会复活了。我们后面学习到的RC和Deployment可以用来动态的创建和销毁Pod。尽管每个Pod都有自己的IP地址,但是如果Pod重新启动了的话那么他的IP很有可能也就变…...

【c++类与对象 】
目录:前言一、基础引入1.类的定义2.类的权限3.类的封装4.类的实例化5.计算类对象的大小结构体内存对齐规则空类的大小二、this指针this引入this指针的特性经典例题三、类的六个默认成员函数1、构造 && 析构构造函数析构函数2、拷贝 && 赋值拷贝构造…...

【C++】内联函数auto范围for循环nullptr
🏖️作者:malloc不出对象 ⛺专栏:C的学习之路 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录前言一、内联函数1.1 内联函数概念1.2…...

运维效率狂飙,都在告警管理上
随着数字化进程的加速,企业IT设备和系统越来越多,告警和流程中断风险也随之增加。每套系统和工具发出的警报,听起来像是一场喧嚣的聚会,各自谈论不同的话题。更糟糕的是,安全和运维团队正在逐渐丧失对告警的敏感度&…...

【每日随笔】中国当前社会阶层 ( 技术无关 | 随便写写 )
文章目录一、阶层划分根据收入划分的阶层根据分工逻辑划分根据权利划分二、根据社会地位和掌握的资源划分的阶层三、赚钱的方式四、如何进入高阶层看了一个有意思的视频 , 讲的是中国当前的社会阶层 , 感觉好有道理 , 搜索了一些资料 ; 参考资料 : 关于中国的社会阶层社会在分…...

【13种css选择器】学css选择器,这一篇就够了
举例形象让你学会,不搞官方话css所有的选择器相邻兄弟选择器后续兄弟选择器后代选择器子代选择器并集选择器(多重选择器)属性选择器伪类选择器伪元素选择器class选择器(类选择器)id选择器*选择器(通配符选择器)标签选择…...

1-1 微服务架构概述
文章目录微服务架构概述1-1. 系统进化理论概述集中式系统:分布式系统1-2. 系统进化理论背景1-3. 什么是微服务架构1-4. 微服务架构的优缺点1-5. 为什么选择 Spring Cloud 构建微服务认识 Spring Cloud2-1. Spring Cloud 是什么2-2. Spring Cloud 的版本2-3 Spring C…...

uniapp传参
//子传父子页面:sumbit() {console.log(this.formData, 传过去的内容对象)let pages getCurrentPages();let prevPage pages[pages.length - 2]; //上一个页面prevPage.$vm.getParams(this.formData); //重点$vmuni.navigateBack();},父页面接收:metho…...

面试官:说说你对 TypeScript 中函数的理解?与 JavaScript 函数的区别?
一、是什么 函数是 JavaScript 应用程序的基础,帮助我们实现抽象层、模拟类、信息隐藏和模块 在 TypeScript 里,虽然已经支持类、命名空间和模块,但函数仍然是主要定义行为的方式,TypeScript 为 JavaScript 函数添加了额外的功能…...

【测试】HD-G2L-IO评估板测试结果表
1. 测试对象HD-G2L-IOT基于HD-G2L-CORE V2.0工业级核心板设计,双路千兆网口、双路CAN-bus、2路RS-232、2路RS-485、DSI、LCD、4G/5G、WiFi、CSI摄像头接口等,接口丰富,适用于工业现场应用需求,亦方便用户评估核心板及CPU的性能。H…...

[2.2.1]进程管理——调度的概念、层次
文章目录第二章 进程管理调度的概念、层次(一)调度的基本概念(二)调度的三个层次(1)高级调度(2)低级调度(3)中级调度补充知识:进程的挂起态与七状…...

【JavaScript UI库和框架】上海道宁与Webix为您提供用于跨平台Web应用程序开发的JS框架及UI小部件
Webix是Javascript库 一种软件产品 用于加速Web开发的 JavaScript UI库和框架 Webix用于跨平台Web应用程序开发的JS框架,为您提供102个UI小部件和功能丰富的CSS/HTML5 JavaScript控件 开发商介绍 Webix团队由由热衷于创建高质量网络产品的专业人士组成ÿ…...

【微信小程序】-- WXS 脚本(二十九)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...

案例19-遇见问题的临时解决方案和最终解决方案
目录1、背景介绍2、两种解决方案的概念1、临时解决方案:2、最终解决方案:3、排查问题过程4、总结站在用户的角度思考作为软件开发者5、升华1、背景介绍 首先说明这是系统很早之前的时候的一个功能,当时和学习通还有很强的耦合关系。在学习通…...

自指(Self-reference)
文章目录1. 在逻辑、数学和计算方面2. 在生物学中3. 在艺术4. 在语言中5. 在流行文化中6. 在法律中自我参照(Self-reference)是一个涉及指代自己或自己的属性、特征或行为的概念。它可以发生在语言、逻辑、数学、哲学和其他领域。 在自然语言或形式语言…...

关于Hanoi塔的实现
关于Hanoi塔的实现 首先,在此之前,我们需要了解一下递归这个东西; 在我看来,递归这个东西就是栈的进出; 向下:进栈回溯:出栈 在进栈之前标记状态,输入到栈中; #incl…...

原始套接字(Raw Socket)
原始套接字允许对较低层次的协议进行访问,如: IP协议,ICMP协议等一般用于自定义协议的实现,处理IP协议没有处理过的数据运输层下IP数据不关注内核是否已有注册的句柄来处理这些数据,都会将这些IP数据复制一份传递给与协议类型匹配的原始套接字,没有的话,直接丢弃该数据,并返回主…...

SparkSQL与Hive交互
SparkSQL与Hive交互一、内嵌Hive应用二、外部Hive应用三、运行Spark SQL CLI四、IDEA操作外部HiveSparkSQL可以采用内嵌Hive,也可以采用外部Hive。企业开发中,通常采用外部Hive。 一、内嵌Hive应用 内嵌Hive,元数据存储在Derby数据库。 &am…...

「题解」日常遇到指针面试题
🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章 🔥座右铭:“不要等到什么都没有了,才下定决心去做” …...

实习生JAVA知识总结目录
一.JAVA基础学习 JAVA知识点全面总结1:零散知识 JAVA知识点全面总结2:面向对象 JAVA知识点全面总结3:String类的学习 JAVA知识点全面总结4:异常类学习 JAVA知识点全面总结5:IO流的学习 JAVA知识点全面总结6&…...

GMPC认证有哪些内容?
【GMPC认证有哪些内容?】GMP(GMP Good Manufacturing Practice)即良好生产规范,最早是美国国会为了规范药品生产而于1963年颁布的。这也是世界上第一部GMP。由于GMP在规范药品的生产,提高药品的质量,保证药品的安全方面效果非常明显…...

D2-Net: A Trainable CNN for Joint Description and Detection of Local Features精读
开源代码:D2-Net 1 摘要 在这项工作中,我们解决了在困难的成像条件下寻找可靠的像素级对应的问题。我们提出了一种由单一卷积神经网络发挥双重作用的方法:它同时是一个密集的特征描述符和一个特征检测器。通过将检测推迟到后期阶段…...

Java基础面试题
目录 一,Java基础 1.1.JDK和JRE有什么区别? 1.2.JAVA中的几种基本类型,各占用多少字节? 1.3.和equals的区别是什么? 1.4.final,finally,finalied有什么区别? 1.15.Java 中操作字符串都有哪些类?它们…...

SQL和MongoDB对比
关系型数据库如MySQL和非关系型数据库MongoDB的对应关系:SQLMongoDBdatabasedatabasetablecollectionrowdocument or Bson documentcolumnfieldindexindextable joins$lookupprimary keyprimary key指定任何唯一的列或列组合作为主键主键会自动设置为_id字段aggrega…...