惠州市建设局网站办事指南,seo关键词排名优化费用,宣传旅游网站建设的观点是什么,建站技术服务文章目录#x1f4d6; 前言1. 统一的列表初始化1.1 { } 花括号初始化#xff1a;1.2 std::initializer_list#xff1a;2. 右值引用2.1 什么是左值和右值#xff1a;2.2 右值的分类#xff1a;2.3 左值引用和右值引用的比较2.3 右值的使用场景#xff1a;2.4 新的类功能 前言1. 统一的列表初始化1.1 { } 花括号初始化1.2 std::initializer_list2. 右值引用2.1 什么是左值和右值2.2 右值的分类2.3 左值引用和右值引用的比较2.3 右值的使用场景2.4 新的类功能前言
在一开始学C之前我们就简单的了解了一下C的发展历史重要的几个结点如下
阶段内容C withclasses类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等C98C标准第一个版本绝大多数编译器都支持得到了国际标准化组织(ISO)和美国标准化协会认可以模板方式重写C标准库引入了STL(标准模板库)C11增加了许多特性使得C更像一种新语言比如正则表达式、基于范围for循环、auto关键字、新容器、列表初始化、标准线程库等C20自C11以来最大的发行版引入了许多新的特性比如**模块(Modules)、协程(Coroutines)、范围(Ranges)、概念(Constraints)**等重大特性还有对已有特性的更新比如Lambda支持模板、范围for支持初始化等
当然在这些之中还发行了其他的版本C还在不断的向后发展。但是现在公司主流使用还是 C98和C11 。
相比于C98/03C11则带来了数量可观的变化其中包含了约140个新特性以及对C03标准中约600个缺陷的修正这使得C11更像是从C98/03中孕育出的一种新语言。相比较而言C11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全不仅功能更强大而且能提升程序员的开发效率公司实际项目开发中也用得比较多所以我们要作为一个重点去学习。
C11官网 传送门 1. 统一的列表初始化
1.1 { } 花括号初始化
在我们之前学的C98中我们初始化一个变量或者是一个数组或一个对象可以是
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;
}现在在C11中我们可以按照如下的方式初始化
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[]的初始化问题
C98只能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;
}而在C11中我们直接可以
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;
}类比C98的隐式类型转换
C98中我们知道单参数的构造函数可以直接给个值直接构造C11可以说是对C98这一特性进行了延伸 单参数构造函数隐式类型转换复习 传送门
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;
}可以理解成C98是单参数隐式类型转换C11是多参数隐式类型转换同时支持了把去掉同样的类似于C98如果不想发生隐式类型转化加上explicit 1.2 std::initializer_list
std::initializer_list的介绍文档 std::initializer_list就像是一个容器一样我们先来看一下其类型 底层大概的样子
是只能读不能写的可以认为在某个区域开了一块空间将花括号中的东西存起来可以认为是常量的数组把它支持起来了 std::initializer_list支持迭代器注意 std::initializer_list内容是不能被改的
为什么突然将到一个容器了呢
首先std::initializer_list是C11新提出来的其次有了std::initializer_list之前学的容器也都支持了用{ }列表初始化 以vector和map为例C11之后就我们之前学的容器可以直接通过{ } 列表初始化了
int main()
{vectorDate v1 { { 2023, 3, 7 }, { 2023, 3, 7 }, { 2023, 3, 7 } };vectorDate v2{ { 2023, 3, 7 }, { 2023, 3, 7 }, { 2023, 3, 7 } };mapstring, string dict1 { { string, 字符串 }, { sort, 排序 } };mapstring, string dict2{ { string, 字符串 }, { sort, 排序 } };return 0;
}先构造一个initializer_list再用initializer_list构造一个vector具体过程 也可以和之前隐式类型转换联系起来也是中间产生了一个临时对象initializer_list再用临时对象去拷贝构造。 2. 右值引用
2.1 什么是左值和右值
传统的C语法中就有引用的语法而C11中新增了的右值引用语法特性所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用都是给对象取别名。
什么是左值什么是左值引用
左值是一个表示数据的表达式如变量名或解引用的指针我们可以获取它的地址 可以对它赋值左值可以出现赋值符号的左边右值不能出现在赋值符号左边定义时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是要将数字转换成字符串然后返回那么这个返回的字符串就是个临时对象除了函数作用域就销毁了传引用返回就拿不到了那块空间已经被销毁了所以这里只能传值返回。 很显然即使是编译器优化了之后也是至少有一次拷贝。
众所周知把大象放到冰箱里一共有三步
第一步打开冰箱门第二步把大象放进去第三步关上冰箱门
那么将大象从一号冰箱放到二号冰箱一共有几步
二臂方法 在二号冰箱里复制一份一模一样的大象再将一号冰箱里的大象干掉 C98拷贝方法函数的传值返回就是按照这种方法这还是编译器优化之后的样子优化之前中间还有个临时变量并且是深拷贝返回效率更低下 聪明办法直接把冰箱的一号和二号的编号调换一下这样就实现了大象在二号冰箱里了 C11右值引用方法当函数传一个将亡值右值返回的时候C11提供了一个移动构造直接用另一个新的对象的指针来接管了原来的对象。
右值引用和移动语义解决上述问题
在string中增加移动构造移动构造本质是将参数右值的资源窃取过来占位已有那么就不用做深拷贝了所以它叫做移动构造就是窃取别人的资源来构造自己 C11中编译器会直接将传值返回识别成一个右值然后调用移动构造 在我们之前没有移动构造的时候我们调用的是拷贝构造之前我们提到过const类型既可以接收右值也可以接收左值所以之前我们是能匹配的上的但是现在现在有了移动构造编译器会匹配最匹配的那一个构造函数。
将一个左值move之后就成了右值右值的资源有可能会被转移 2.4 新的类功能
默认成员函数
原来C类中有6个默认成员函数 构造函数 析构函数 拷贝构造函数 拷贝赋值重载 取地址重载 const 取地址重载
C11 新增了两个默认成员函数
移动构造函数和移动赋值运算符重载移动构造和移动赋值是有条件的并且默认生成的达到不了我们想要的效果所以一般我们自己实现
要求 如果你没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数对于内置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动构造如果实现了就调用移动构造没有实现就调用拷贝构造。如果你没有自己实现移动赋值重载函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数对于内置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动赋值如果实现了就调用移动赋值没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)如果你提供了移动构造或者移动赋值编译器不会自动提供拷贝构造和拷贝赋值。 STL的容器C11以后都提供移动构造和移动赋值右值引用 移动构造补齐了C 传参和传返回值的最后一块短板。
STL容器中的各种插入也用到了右值引用
当这些容器的元素是某个对象的时候插入的话要new一个新的元素也会产生深拷贝的问题所以这里用到右值引用将会非常方便 int main()
{listYY::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成右值不然会造成资源的丢失