C++进阶学习:C++11特性

C++11是C++语言的一个重要版本,引入了许多新的特性和改进。接下来进行这些新特性的学习!

1.nullptr的引入

在C语言中,NULL表示空地址。而C++中NULL被定义为字面量0。

这里我们通过打印x的类型名,发现NULL的类型名是int,而对于NULL既能够表示指针值,又可以能表示int值,所以C++11引入了nullptr关键字来表示空指针!

2.列表初始化

2.1.语法层面

我们先通过一段代码来学习一下,列表初始化这个新特性


struct Point
{int _x;int _y;
};class Date
{
public: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;
};// 语法层面上
// 一切都可以用列表初始化
// 并且可以省略掉=
void part1_0()
{int i = 0;int j = { 0 };int k{ 0 };int array1[] = { 1, 2, 3, 4, 5 };int array2[]{ 1, 2, 3, 4, 5 };int array3[5] = { 0 };int array4[5]{ 0 };Point p1 = { 1, 2 };Point p2{ 1, 2 };// 日期类需要传入参数Date d1(2023, 11, 25);// 类型转换  构造+拷贝构造  优化直接构造Date d2 = { 2023, 11, 25 };Date d3{ 2023, 11, 25 };const Date& d4 = { 2023, 11, 25 };Date* pd1 = new Date[3]{ d1, d2, d3 };Date* pd2 = new Date[3]{ {2022, 11, 25}, {2022, 11, 26}, {2022, 11, 27} };}

通过这段代码的学习,我们学习到了以后我们定义变量构造对象时可以不用在借助=操作符,可以直接通过{ }来实现,但是列表初始化只是为了实现这个功能吗?当然不是的,我们接下来通过底层来探讨列表初始化。


2.2.列表初始化的作用

如图:我们在日期类中,定义初始化函数是通过传入3个int类型的参数,当我们传入4个参数时会报错:

  1. “初始化”: 无法从“initializer list”转换为“Date”
  2. 无构造函数可以接受源类型,或构造函数重载决策不明确

这两个问题第二个是根本上的问题,因为传入的参数数量不匹配。这里涉及先进行构造再进行拷贝构造,来传入参数实现!

  • 第一个报错是什么意思呢?
  • 为什么vector类型可以随意的增加参数的数量而不会报错呢?

答:因为vector以及大部分的STL容器中在C++11版本后支持initializer list这个结构,不支持这个结构的结构体,无法进行多参数的调整转化,传入参数需要与构造函数参数个数匹配。

 为了进一步验证这个initializer list结构的作用,我们通过手搓一个vector但是不实现initializer list模块,来探讨一下能不能进行随意地增加参数!重生之C++学习:vector-CSDN博客

我们在这个博客中最终的代码,对这段代码进行测试

结果是:我们无法的实现参数的随意增加!!!

当我们在vector模块增加一个由initializer_list支持的构造函数,我们就能够实现我们的随意增加减少我们传入的参数了!

my_vector(initializer_list<T> il)
{reserve(il.size());for (auto& e : il){push_back(e);}
}

讲到这里,我们已经知道了列表初始化的作用:简化代码,提供了更灵活的初始化方式。 

map<string, string> dict = {{"insert", "插入"}, {"get","获取"} };
for (auto& kv : dict)
{cout << kv.first << ":" << kv.second << endl;
}

如上,我们通过初始化列表对map的便捷初始化 


2.3.initializer list

initializer list本质上就是一个类,内部封装着这几个函数

 通过这段代码我们发现il1的类型是初始化列表,也就是我们通过方括号的形式传入参数的本质就是通过传入初始化列表这个对象给对应的容器,来实现容器的构造,那么大部分的容器也就是通过支持传入initializer list这个对象的构造函数来实现{ }的多个参数的传入,而对于没有实现这个构造函数的自然就必须按照自身的构造函数规则进行了。

// the type of il is an initializer_list 
auto il1 = { 10, 20, 30, 40, 50 };
cout << typeid(il1).name() << endl;

3.auto和decltype

auto 和 decltype都可以推导对象的类型,而auto是用来定义变量时,作为一个简易的语法糖,不能够用来实例化对象。

	// auto推导类型,其中i是已定义的一个int类型,d是double类型auto j = i;auto ret = i * d;// 类型以字符串形式获取到cout << typeid(j).name() << endl;cout << typeid(ret).name() << endl;

打印内容为:int和double,这里auto分别推导出j、ret两个变量的类型

	// auto在设置时并没有实例化作为实际类型的能力,auto应用场景不足vector<auto ret> v1;auto (ret) y;

假设我们在某些场景中,需要推导出一个复合类型的实际类型是什么,并且将这个类型用STL容器存放,显然在上面的讲解中我们发现auto无法胜任这个场景。所以C++11提供了decltype这个关键字。

	// 用ret的类型去实例化vector// decltype可以推导对象的类型。// 可以用来模板实参,或者再定义对象vector<decltype(ret)> v;v.push_back(1);v.push_back(1.1);for (auto e : v){cout << e << " ";}cout << endl;

4. 右值引用

4.1.左值和右值

左值(lvalue)

左值是指那些可以出现在赋值操作左侧的表达式。它们通常表示一个对象的身份,即它们在内存中的位置。左值有一个持久的存储位置,并且可以通过取地址操作符 & 获得其地址。大部分变量(包括全局变量、局部变量、静态变量等)都是左值。

	// 左值 表示可以取地址的值int i = 0;int j = i;

右值(rvalue)

右值是指那些只能出现在赋值操作右侧的表达式。它们通常表示临时对象或即将被销毁的对象,因此没有持久的存储位置。右值不能通过取地址操作符 & 获取其地址。在C++11之前,右值主要包括字面量、临时对象以及函数调用返回的非引用对象。

	// 右值10;int func();i + j;func();

一言以蔽之:只要某一个值能取到地址的就是左值,右值无法取到地址,因为临时变量和将要销毁的变量的地址在当前生命周期即将释放。 


4.2.左值和右值引用

	// 左值引用int& r_i = i;int& r_j = j;// 10虽然是右值,但是const int&属于左值引用,对常量const int& r_c = 10;// 右值不能进行左值引用// int& r1 = 10;// int& r2 = i + j;// 右值引用,对临时值、常量进行引用int&& rr1 = 10;int&& rr2 = i + j;int&& rr3 = func();// 右值引用不能绑定左值// int&& rr4 = i;// 通过move可以将左值进行右值引用int&& rr4 = move(i);

 左值引用的语法是一个&,而右值引用是&&,并且左值引用不能绑定右值,右值引用不能绑定左值,但是我们可以通过move函数实现右值引用绑定左值。 


4.3.右值引用和移动语义

在C++11中,引入了右值引用(rvalue reference)的概念,使用 && 符号表示。右值引用允许我们绑定到右值,从而可以高效地处理临时对象或即将被销毁的对象,避免不必要的拷贝操作。这种技术被称为移动语义(move semantics)。

通过移动语义,我们可以将资源(如动态分配的内存、文件句柄等)从一个对象“移动”到另一个对象,而不是复制它们。这通常比复制更快,并且可以避免不必要的资源分配和释放。

那么接下来我们通过两个个场景的学习来体会一下右值引用和移动语义……

首先我们要知道为什么我们可以使用左值引用?左值引用需要可以取得到左值的地址,因为左值的在当前模块的声明周期较长。

int func()
{int i = 10;int& j = i;return i;
}

 在这个场景中,我们能修改func的返回类型为int&(左值引用返回值)吗,很显然是不行的,因为i变量在函数模块中可以被引用为j,这时他的生命周期较长。而当我们return时,这个i已经变成了即将销毁的对象(将亡值),所以不能够被引用了,这也就是左值引用无法解决的场景。

那么这时我们修改一下,将这个函数变成右值引用返回,这时这个问题就解决了。

int&& func()
{int i = 10;int& j = i;return move(i);
}
int main()
{int&& rr1 = func();
}

联系一下右值引用和移动语义的作用,会减少不必要的拷贝工作,如图我们发现在func中的i的地址最终移动到了rr1中


但是减少不必要的拷贝没有得到较好的体现, 接着我们用新的场景来体现,代码部分还是从以往博客的综合处获得   重生之C++学习:string的实现-CSDN博客

void test()
{myString::my_string s1;s1 = myString::to_string(1234);for (auto& e : s1){cout << e << " ";}
}

我们写一个测试函数,在这个函数中我们想要实现的逻辑创建字符串对象s1,然后通过to_string函数实现数字转化为字符串形式。接下来我们来研究一下实现这个功能的代码

主要是因为我们当前的my_string这个类中,只支持了左值引用的拷贝构造函数,和=操作符重载,而当我们希望将右值的这个ret通过这两个函数传入給s1时,需要进行拷贝构造出一个临时对象,然后在=给s1,这里就多了一步临时变量的创建和销毁,因此在上图中我们发现ret和s1对应的_str存在两份地址,但是明明表示的就是一个东西。

这里的原因是:编译器会默认将右值通过构造一个新的左值来进行左值引用,所以并不是右值可以作为左值引用类型的参数。所以就会出现这个新的左值可能带来不必要的拷贝,尤其是一些复杂的类型(因为内部可能维护了很多变量)。

接下来我们给这个string支持右值引用,这里利用了编译器会自动选择更加符合的类型的函数进行调用,那么对于右值引用类型就不会去创建一个临时变量,来实现左值引用传入了。那么就减少了这一步临时变量的创建和释放

my_string(my_string&& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
my_string& operator=(my_string&& s)
{	std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);return *this;
}

接着我们再次打开调试窗口 发现 我们支持右值引用后,ret和s1的_str共用一块地址了,也就是地址实现了从一个将要释放的快要结束生命周期的临时变量移动到了另一个对象这个就是移动语义。

所以使用“右值引用”可以帮助我们完成一些左值引用存在缺陷的场景,这些场景往往都是需要额外开辟和释放空间来实现,右值往左值的转化,来间接进行左值的操作,而C++11提供右值引用这个语法来进行右值直接向生命周期长的对象进行转移。

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

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

相关文章

算法与数据结构要点速学——时间复杂度(大 O)

时间复杂度 (大 O) 首先&#xff0c;我们来谈谈常用操作的时间复杂度&#xff0c;按数据结构/算法划分。然后&#xff0c;我们将讨论给定输入大小的合理复杂性。 数组&#xff08;动态数组/列表&#xff09; 规定 n arr.length, 在结尾添加或删除元素&#xff1a;O(1)从任意…

【C++】力扣OJ题:找出只出现一次的数字

Hello everybody!这是我第一次写关于OJ题目的博客&#xff0c;因为正好学到完了C的STL库&#xff0c;就顺手刷了一些OJ题。 我今天要介绍的题目虽然是力扣上的简单题&#xff0c;但思想很巧妙&#xff0c;我觉得有必要和大家分享一下&#xff01; 1.题目 2.代码 class Solut…

数据中心配电解决方案及项目案例

安科瑞电气股份有限公司 祁洁 15000363176 一、方案背景 为了确保数据中心供电的可靠性&#xff0c;通常会将数据中心的配电关键组件进行冗余设计&#xff0c;关键组件&#xff08;例如 UPS 单元、冷却系统和备用发电机&#xff09;被复制。同时将这些配电设备纳入到监控系…

视频批量高效剪辑,支持将视频文件转换为音频文件,轻松掌握视频格式

在数字化时代&#xff0c;视频内容日益丰富&#xff0c;管理和编辑这些视频变得愈发重要。然而&#xff0c;传统的视频剪辑软件往往操作复杂&#xff0c;难以满足高效批量处理的需求。现在&#xff0c;一款全新的视频批量剪辑神器应运而生&#xff0c;它支持将视频文件一键转换…

Day01-环境准备与镜像案例

Day01-环境准备与镜像案例 1. 容器架构1.1 Iaas Paas Saas (了解)1.2 什么是容器1.3 容器vs虚拟机1.4 Docker极速上手指南1&#xff09;配置docker源(用于安装docker)2&#xff09;docker下载镜像加速的配置3&#xff09;自动补全 1.5 Docker C/S架构1.6 Docker的镜像管理1&…

Java链式编程

一&#xff1a;链式编程 可以简化编程。代码简洁。 定义&#xff1a; 链式编程&#xff1a;顾名思义&#xff0c;链子嘛。它是一种编程范式&#xff0c;它允许将多个函数或操作连接在一起&#xff0c;形成一个链条&#xff0c;以执行复杂的操作。 优点&#xff1a; 编程性…

【Gradio】Could not create share link

【Gradio】Could not create share link 写在最前面在服务器端一直运行一个Python脚本解决&#xff1a;下载frpc_linux_amd64文件&#xff0c;并添加权限原理 完整过程&#xff1a;先找gradio库位置&#xff0c;然后发现缺失文件1. 打开终端2. 使用 find 命令查找 gradio 目录3…

vue学习日记22:非父子通信(拓展)-provideinject

一、概念 二、实践 代码 App <template><div class"app">我是APP组件<button click"change">修改数据</button><SonA></SonA><SonB></SonB></div> </template><script> import SonA …

Java SDK 使用示例

我们使用同样的 Maven 模板去创建 use-test-sdk 项目&#xff0c;不同的是&#xff0c;我们需要在 use-tset-sdk 下创建一个 lib 文件夹&#xff0c;用来存放 test-sdk.jar 文件。 我们将 test-sdk.jar 拖放到 use-test-sdk/lib 下&#xff0c;然后我们就可以编写代码了&#…

每日OJ题_BFS解决最短路④_力扣675. 为高尔夫比赛砍树

目录 力扣675. 为高尔夫比赛砍树 解析代码 力扣675. 为高尔夫比赛砍树 675. 为高尔夫比赛砍树 难度 困难 你被请来给一个要举办高尔夫比赛的树林砍树。树林由一个 m x n 的矩阵表示&#xff0c; 在这个矩阵中&#xff1a; 0 表示障碍&#xff0c;无法触碰1 表示地面&…

知道XRD标准品PDF卡片号,如何直接导出标准物质数据,简单快速一分钟完成(附jade下载安装方法)

知道XRD标准品PDF卡片号&#xff0c;如何直接导出标准物质数据&#xff08;附jade下载安装方法&#xff09; 网上找到的方法都是先检索再导出&#xff0c;我的样品根本检索不到&#xff0c;但是根据参考文献知道了自己的pdf卡片号&#xff0c;可通过jade直接导出数据 1.请安装…

【Linux】磁盘扩容到根目录逻辑卷(LVM)

目录 一、物理卷和逻辑卷 1.物理卷和逻辑卷的区别 2.在Linux系统中查看所有物理卷的信息 3.在Linux系统中查看所有逻辑卷的信息 二、文件系统 三、实操-对root&#xff08;/&#xff09;目录进行扩容 1.使用lsblk命令查看新加入的磁盘信息 2.fdisk -l命令查看系统中磁盘…

git报错

这里写自定义目录标题 git报错Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 有一个原因就是在github上设置对应密钥时&#xff0c;有一个key获取应该设置为…

【Redis 神秘大陆】005 常见性能优化方式

五、Redis 性能优化 5.1 系统层面的优化 https://github.com/sohutv/cachecloud/blob/main/redis-ecs/script/cachecloud-init.sh initConfig() {# 支持虚拟内存分配sysctl vm.overcommit_memory1# 最大排队连接数设置为 511&#xff0c;一般默认是 128echo 511 >/proc/sy…

男生穿什么裤子最百搭?适合男生穿的裤子品牌测评分享

每个伙伴们想必经常都会选择一些裤子&#xff0c;但现在市面上的裤子品牌也实在太多了&#xff0c;好不容易选到了几件好看的裤子&#xff0c;结果质量却很不好。主要就是因为现在有太多商家为了利润而使用一些舒适性、质量差的面料&#xff0c;那么今天就给大家分享一些质量上…

引导和服务(2)

服务 1.systemd服务的简要介绍 &#xff08;1&#xff09;对比5 6 可以解决依赖关系并行启动 &#xff08;2&#xff09;按需启动 &#xff08;3&#xff09;自动解决依赖关系 负责在系统启动或运行时&#xff0c;激活系统资源&#xff0c;服务器进程和其它进程 2.System…

操作系统(第五周 第一二堂总结)

目录 回顾 前景知识 概述 定义 进程和线程的关系 进程和线程的区别 线程优缺点 优点&#xff1a; 缺点&#xff1a; 易混概念 线程实现方式 线程的类型&#xff1a; ​编辑 多线程模型&#xff1a; 线程函数 头文件&#xff1a; 线程创建函数&#xff1a; 线…

vscode i18n Ally插件配置项

.vscode文件&#xff1a; {"i18n-ally.localesPaths": ["src/lang"], //显示语言&#xff0c; 这里也可以设置显示英文为en,// 如下须要手动配置"i18n-ally.keystyle": "nested", // 翻译路径格式 (翻译后变量格式 nested&#xff1a…

氟化钡与盐酸反应不

结论&#xff1a;反应 氟化钡 名称   中文名称&#xff1a;氟化钡   英文别名&#xff1a;Bariumfluoride 化学式   BaF2 相对分子质量   175.32 性状   无色透明立方结晶或白色粉末。溶于盐酸、硝酸、氢氟酸和氯化铵溶液&#xff0c;微溶于水。 相对密度4.83。 熔…

MongoDB 使用

1 引用依赖包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>2 配置文件配置mongodb资料 # MongoDB连接信息 spring.data.mongodb.host 192.168.23.…