C++进阶篇6---C++11新语法

目录

目录

一、统一的列表初始化

 二、声明

1.auto

2.decltype

3.nullptr

三、范围for

四、STL中的变化

五、右值引用和移动语义(重点)


一、统一的列表初始化

在c++11之前,我们能用{}初始化数组和结构体

struct Point {int x;int y;
};
int main()
{int a[] = { 1,2,3,4 };Point p = { 1,1 };return 0;
}
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自
定义的类型,使用初始化列表时,可添加等号(=),也可不添加
struct Point
{int _x;int _y;
};
int main()
{int x1 = 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// C++11中列表初始化也可以适用于new表达式中int* pa = new int[4] { 0 };return 0;
}//创建对象时也可以使用列表初始化方式调用构造函数初始化
class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};int main()
{Date x(2023, 11, 30);Date y = { 2023,11,30 };Date z{ 2023,11,30 };return 0;
}

这些功能的实现和initializer_list这个容器有关

使用场景:

std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器增加了std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator= 的参数,这样就可以用大括号赋值
int main()
{vector<int>v = { 0,1,2,3,4 };list<int>l = { 2,3,5,6,30 };map<string, string>mp = { {"string","字符串"} ,{"sort","排序"} };for (auto x : v){cout << x << " ";}cout << endl;for (auto x : l){cout << x << " ";}cout << endl;for (auto x : mp){cout << x.first << ":" << x.second << endl;}v = { 1,2,34,5,6 };for (auto x : v){cout << x << " ";}cout << endl;return 0;
}

这个{}初始化和赋值不难实现,我就拿之前写过的模拟实现的vector来举个例子,这里放关键的函数,如果对vector的模拟实现感兴趣可以去看C++入门篇8,里面有完整模拟实现代码


 二、声明

1.auto

C++11中废弃auto原来的用法,将其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型,简单来说,就是能自动推导数据类型,相信大家都很熟悉,这里就不多说了

2.decltype

关键字decltype将变量的类型声明为表达式指定的类型
int main()
{int x = 1;double y = 2.2;decltype(x * y) ret = x * y;//这个用法和auto没啥区别vector<decltype(x * y)>v;//这里只能用decltype//vector<auto>v,错误写法return 0;
}

也就是说,当我们需要类型作为参数时,只能用decltype

3.nullptr

这个也不多说,因为C++官方将NULL定义为了0,所以加了一个nullptr表示空指针


三、范围for

底层就是迭代器遍历容器。

int main()
{vector<int>v{ 1,2,3,4,5,6 };for (auto& e : v)//范围forcout << e << " ";return 0;
}

四、STL中的变化

多了静态数组、单链表和哈希表,还有一些接口,如cbegin、cend、emplace等

大致说说这些容器的情况:静态数组array比较鸡肋,因为vector完全够用,forward_list单链表也作用不大,也就是比较省空间,哈希表还是很有用的


五、右值引用和移动语义(重点)

我们之前学的引用又被叫做左值引用,其实不管是什么引用,都是给对象取别名

什么是左值?什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取名
int main()
{// 以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;// 以下几个是对上面左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pval = *p;return 0;
}
什么是右值?什么是右值引用?
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。
int main()
{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;
}

注意:右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说被右值引用过的右值具备了左值的性质,即可以被取地址+被赋值,如果不想被赋值可以用const修饰右值引用,这个了解一下,实际中右值引用的使用场景并不在于此,这个特性也不重要。

int main()
{double&& x = 1.1;const double&& y = 1.2;x = 1.3;y = 1.5;//错误return 0;
}

那么左值引用能引用右值吗?右值引用能引用左值吗?

int main()
{//左值引用可以引用右值,但要加const修饰,本质是权限的放大问题//int& t1 = 1;//不行const int& t2 = 1;//右值引用无法引用左值int x = 0;//int&& rx = x;//const int&& rx = x;int&& rx = move(x);//但可以引用被move以后的左值return 0;
}

总结:

1.左值引用只能引用左值,但加上const的左值引用既能引用左值,也能引用右值

2.右值引用只能引用右值,但是右值引用可以引用被move过的左值

了解了上面的内容之后,我们来谈谈右值引用的作用和使用场景

C++中引入引用的概念本意是为了节省空间,左值引用已经满足了大部分的场景,如传参,做返回值(该对象在出了函数作用也还存在),但是如果该对象出了函数作用域后就销毁呢?如果不需要深度拷贝还好,一旦需要深度拷贝,就会浪费开辟空间需要的时间,如下面的场景---基于我在C++初级篇7string中附上的代码

我们可以很明显的感觉到在上面的过程中,空间的创建其实是不必要的,我们可以直接将str的资源直接交给s,没有必要另外创建两个对象

那么如何实现呢?

string(string&& tmp)//移动构造:_str(nullptr), _size(0), _capacity(0)
{swap(tmp);
}string& operator=(string&& tmp)//移动赋值
{swap(tmp);return *this;
}

这里的右值又称为将亡值,即生命周期快要结束,那么我们就可以将这个变量的资源交给需要它的对象,如下图

注意:string&&和string&虽然都是引用,但是类型是不同的,所以虽然const string&也能引用右值,但是C++的函数调用要求使用参数最匹配的函数,所以左值和右值的调用会分别调用最匹配的

当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中std::move()函数位于 头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义, 这里的强转指的是函数返回值
template<class _Ty>//下面的函数参数和万能引用有关,后面再说,这里只要记住函数返回值被强转成了右值
inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT
{// forward _Arg as movablereturn ((typename remove_reference<_Ty>::type&&)_Arg);
}
//要谨慎使用move,不然可能出现下面的情况 
int main()
{zxws::string s1("hello world");// 这里s1是左值,调用的是拷贝构造zxws::string s2(s1);// 这里我们把s1 move处理以后, 会被当成右值,调用移动构造// 但是这里要注意,一般是不要这样用的,因为我们会发现s1的// 资源被转移给了s3,s1被置空了。zxws::string s3(std::move(s1));return 0;
}

 

 STL容器插入接口函数也增加了右值引用版本,提高了插入效率


函数模板中的万能引用和完美转发

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }//函数模板
template<class T>
void Perfect(T&& x)//万能引用,既可以传左值,也可以传右值
{Fun(forward<T>(x));//forward<T>(x)在传参的过程中保持了x的原生类型属性,称为完美转发//可能有人觉得多此一举,但是上面我曾说过右值引用过的右值具有左值的属性,//所以如果写Fun(x)在传参时,传右值结果会是传的左值,//而如果写Fun(move(x)),则左值也会变成右值//所以这里用完美转发forward<T>(),解决所有问题
}int main()
{int x = 1;Perfect(x);const int y = 0;Perfect(y);Perfect(move(x));Perfect(move(y));return 0;
}

注意:只有当T&&中的T是被推导出来的时候,T&&才是万能引用


六、新的类功能

C++11新增了两个默认成员函数---移动构造和移动赋值

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

 还多了几个关键字:

  • 强制生成默认函数的关键字default
  • 禁止生成默认函数的关键字delete
  • 继承和多态中的final与override
class A {
public:A(){}A(const A& a) = delete;~A() = default;//final 和 override 在多态中讲过
};

七、可变参数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}//sizeof...(args) 可以查参数个数

 举个例子

void _ShowList()
{cout << endl;
}template <class T, class ...Args>
void _ShowList(T val, Args...args)
{cout << val << " ";_ShowList(args...);
}template <class ...Args>
void ShowList(Args... args)
{_ShowList(args...);
}int main()
{ShowList(1, 2.2, 'x', "hhhh");//打印return 0;
}

上面的这段代码可以打印不同类型的参数,大家可以看一下,带入递归,理解一下

解析:上面的代码可以看成是模板参数的递归,正常的递归函数都是需要有递归出口的,而上面模板函数的递归出口在于参数列表为空,下面画个图帮大家理解一下

这个还有另一种打印方式

template <class T>
int Print(T val)
{cout << val << " ";return 0;
}
template <class ...Args>
void ShowList(Args... args)
{int a[] = { Print(args)... };//{(Print(args), 0)...}将会展开成((Print(arg1),0),(Print(arg2),0), (Print(arg3),0), etc... )//利用创建数组需要知道开辟空间大小,强行让编译器执行打印函数cout << endl;
}int main()
{ShowList(1, 2.2, 'x', "hhhh");return 0;
}

上面的打印代码确实很难理解,也很奇怪,无法理解的话,就暂且认为它是一种语法规定就行

实际上,可变参数列表的用处不在上面所说的打印,而是在于emplace系列接口的实现,给一个emplace_back的函数声明

template <class... Args>
void emplace_back (Args&&... args);

它既支持可变参数,也支持万能引用,那么相对正常的插入,它的优势体现在哪里?

就单纯拿push_back和emplace_back来比较,我写一个list中emplace_back的模拟实现给大家看看

如果看不太明白,可以用emplace_back("hello",1)和push_back(make_pair("hello",2))去代入 

其实push_back()也就比emplace多了一次移动拷贝,效率上差不了多少(在需要深度拷贝的时候),当不需要深度拷贝且类比较大时,emplace的效率就会比较高

(C++11语法较多,其他重要的语法会在后续章节进行讲解,敬请期待)

未完待续…………

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

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

相关文章

机器学习 - 导论

简单了解 机器学习关于数据集的概念 、

HCIP —— 双点重发布 + 路由策略 实验

目录 实验拓扑&#xff1a; 实验要求&#xff1a; 实验配置&#xff1a; 1.配置IP地址 2.配置动态路由协议 —— RIP 、 OSPF R1 RIP R4 OSPF R2 配置RIP、OSPF 双向重发布 R3配置RIP、OSPF 双向重发布 3.查询路由表学习情况 4.使用路由策略控制选路 R2 R3 5.检…

Linux shell编程学习笔记32:declare 命令

0 前言 在 Linux shell编程学习笔记16&#xff1a;bash中的关联数组https://blog.csdn.net/Purpleendurer/article/details/134053506?spm1001.2014.3001.5501 中&#xff0c;我们在定义关联数组时使用了declare命令。 其实&#xff0c;declare命令的功能不只是定义定义关…

排序算法介绍(三)选择排序

0. 简介 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完。选择排序是不稳…

超大规模集成电路设计----学习框架(一)

本文仅供学习&#xff0c;不作任何商业用途&#xff0c;严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----学习框架&#xff08;一&#xff09; 这门课在学什么&#xff1f;这门课该怎么学&#xf…

Python---函数递归---练习:猴子吃桃问题(本文以递归算法 解法为主)

相关链接&#xff1a;Python---函数递归---练习&#xff1a;斐波那契数列&#xff08;本文以递归算法为主&#xff09;-CSDN博客 案例&#xff1a;猴子吃桃问题 猴子吃桃问题。猴子第1天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。…

类和对象——(5)定义对象数组

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 芳华没有草稿纸&#xff0c;我们永久不…

openEuler学习05-ssh升级到openssh-9.5p1

openEuler的版本是openEuler 20.03&#xff0c;ssh的版本是OpenSSH_8.2p1 [roottest ~]# more /etc/os-release NAME"openEuler" VERSION"20.03 (LTS-SP3)" ID"openEuler" VERSION_ID"20.03" PRETTY_NAME"openEuler 20.03 (LTS-…

【Google2023】利用TiDE进行长期预测实战(时间序列密集编码器)

一、本文介绍 大家好&#xff0c;最近在搞论文所以在研究各种论文的思想&#xff0c;这篇文章给大家带来的是TiDE模型由Goggle在2023.8年发布&#xff0c;其主要的核心思想是&#xff1a;基于多层感知机&#xff08;MLP&#xff09;构建的编码器-解码器架构&#xff0c;核心创…

Python---函数递归---练习:斐波那契数列(本文以递归算法为主)

编程思想&#xff1a; 如何利用数学模型&#xff0c;来解决对应的需求问题&#xff1b;然后利用代码实现对应的数据模型。 算法&#xff1a;使用代码实现对应的数学模型&#xff0c;从而解决对应的业务问题 程序 算法 数据结构 在经常使用的算法中&#xff0c;有两种非常…

RC低通滤波电路直接带载后会发生什么?

1、滤波的含义 滤波是频域范畴&#xff0c;它说的是不同频率的信号经过一个电路处理后&#xff0c;信号发生变化的问题&#xff0c;变化包含了原始信号幅值和相位的变化&#xff0c;滤波电路对信号的幅值做出的响应称为幅频响应&#xff0c;对信号相位做出的反应称为相频响应。…

设计模式---第五篇

系列文章目录 文章目录 系列文章目录前言一、知道观察者模式吗?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、知道观察者模式吗? 答:观察者模式是定义对…

逸学java【初级菜鸟篇】12.网络通讯编程

hi&#xff0c;我是逸尘&#xff0c;一起学java吧 目标&#xff08;任务驱动&#xff09; 请练掌网络通讯的内容。 局域网和互联网 局域网英文&#xff1a;Local Area Network&#xff0c;缩写&#xff1a;LAN&#xff0c;是指一群通过一定形式连接起来的计算机&#xff0c;…

Java封装讯飞星火大模型历险记

问题描述与分析 现状描述与目标 在使用讯飞星火大模型API的过程中&#xff0c;API的返回结果在可以在其他线程中进行分次打印&#xff0c;但是在main方法中直接打印返回结果&#xff0c;显示为空。这种情况下不利于二次封装&#xff0c;希望在main方法中获取完整的API返回结果…

Linux——基本指令(一)

写在前面&#xff1a; 我们云服务器搭建的Linux系统&#xff0c;使用的镜像版本CentOS 7.6,使用的Xshell远程连接云服务器 前面我们使用超级管理员root账号登录&#xff0c;一般我们使用普通用户登录&#xff0c;那么如何创建新用户呢&#xff1f; 1.创建新用户 &#xff08…

Ubuntu22.04无需命令行安装中文输入法

概要&#xff1a;Ubuntu22.04安装完成后&#xff0c;只需在设置中点点点即可完成中文输入法的安装&#xff0c;无需命令行。 一、安装中文语言包 1、点击屏幕右上角&#xff0c;如下图所示。 2、点击设置 3、选择地区与语言&#xff0c;点击管理已安装的语言 4、点击安装 5、输…

❀My学习Linux命令小记录(10)❀

目录 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 37.expr指令 38.iperf指令 39.telnet指令 40.ssh指令 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 功能说明&#xff1a;控制文件内容输出时所占用的屏幕宽度&#xff0c…

上门按摩APP小程序,抓住机遇创新服务新模式;

上门按摩APP小程序&#xff1a;抓住机遇&#xff0c;创新服务新模式&#xff1b; 随着现代人对生活质量要求的提高&#xff0c;上门按摩服务正成为一种新的、受欢迎的生活方式。通过APP小程序&#xff0c;用户可以轻松预约按摩服务&#xff0c;解决身体疲劳问题&#xff0c;享受…

Python程序员入门指南:学习时间和方法

文章目录 标题Python程序员入门指南&#xff1a;学习时间、方法和就业前景学习方法建议学习时间 标题 Python程序员入门指南&#xff1a;学习时间、方法和就业前景 Python是一种流行的编程语言&#xff0c;它具有简洁、易读和灵活的特点。Python可以用于多种领域&#xff0c;如…

设计模式基础(1)

目录 一、设计模式的定义 二、设计模式的三大类别 三、设计模式的原则 四、主要设计模式目录 4.1 创建型模式&#xff08;Creational Patterns&#xff09; 4.2 结构型模式&#xff08;Structural Patterns&#xff09; 4.3 行为型模式&#xff08;Behavioral Patterns&…