C++11常用知识分享(一)【列表初始化 || 简化声明 || 范围for || 左右值 || 可变参数模板】

目录

一. 列表初始化

1)用法

2) initializer_list

小节: 

二,简化声明

1) ,auto

2) ,decltype类

3),nullptr

三,范围for

四,C++11后,STL容器变化

五,左值与右值

1. 左值

2. 右值

3,左值与右值之间的比较

4. 右值引用使用场景

(1,将死右值的移动语义操作 

(2. 编译器传值返回优化

(3,将亡值做参数

(4. 完美转发

 5,默认成员函数

(1. 默认成员函数的强制生成

(2. 默认成员函数的禁止调用生成


嗨!收到一张超美的风景图,希望你每天都能顺心!  

一. 列表初始化

首先,我们需要区分的是,什么是初始化列表与列表初始化。 前者是在类对象创建时,对成员变量进行初始化。后者是C++11优化后添加的新功能。

C++中为了满足泛型编程,基础类型如int, double等内置类型都被重写成了类。这些类被称为包装类,它们提供了一些额外的功能,比如重载运算符、提供类型转换等,以便更好地支持泛型编程。

从下面的用法,我们其实可以窥见一二:

1)用法

其实在C++98中,标准允许使用花括号  {}  对数组或者结构体元素进行 统一的列表初始值设定。比如:
struct Point
{int _x;int _y;
};
int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };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 };// C++11中列表初始化也可以适用于new表达式中int* pa = new int[4]{ 0 };//自定义类型Point p{ 1, 2 };map<string, string> z1 = { {"li", "a"}, {"ze", "b"} };map<string, string> z2{ {"li", "a"}, {"ze", "b"} };return 0;
}
那这是如果实现的呢?

2) initializer_list

其实这个{ }本质上是一个特殊类,叫:std::initializer_list

介绍文档:initializer_list - C++ Reference (cplusplus.com)

那自定义类型,比如:vector如何利用initializer_list来实现列表初始化的呢?? 下面是vector构造函数,C++11的构造方法,在下面我们能看到我们要找的构造 

 

自己制作一个支持initializer_list的构造函数还是比较简单的:

但是我们也要注意:initializer_list中的变量是常量,在静态区,因此这些数据仅仅只能做数据值传递,无法修改。 

小节: 

C++11后,STL容器已经全部支持std::initializer_list列表初始化,作为构造函数的参数,使初始化容器对象就更方便了

二,简化声明

1) ,auto

对于我们来说auto已经是老朋友了,在C++11之前,变量的类型必定要显式地指定。然而,C++11引入了auto关键字,允许编译器根据变量的初始化表达式推断其类型。这意味着可以更加简洁地声明变量,减少了代码的冗余性和提高了代码的可读性。

例子 :

    map<string, string> z1 = { {"li", "a"}, {"ze", "b"} };// auto遍历for (auto& e : z1)  {cout << e.first << endl;}// 推导迭代器auto it = z1.begin();

2) ,decltype类

decltype的主要作用是推断表达式的类型,并且保留表达式的const和引用属性(auto 不能保留)。这使得我们可以使用decltype来声明变量、函数返回类型,以及在模板中推断函数返回类型等。

在泛型编程中,decltype可以帮助我们避免重复输入类型信息,提高代码的可读性和可维护性。它还可以用于推断lambda表达式的返回类型,以及在模板元编程中进行类型推断。

总之,decltype类的意义:提供了一种灵活的类型推断机制,使得C++编程更加方便和高效

3),nullptr

在C++11中添加了nullptr是为了解决空指针的歧义问题。在之前的C++版本中,使用NULL来表示空指针可能会引发一些问题,因为NULL可能被定义为0或者(void*)0,这样就会导致一些类型转换的问题

使用nullptr可以明确地表示空指针,避免了歧义,同时也可以提高代码的可读性和安全性。

三,范围for

范围for我们在遍历数据时,经常使用,其底层原理是迭代器。有了范围for后,使用范围for循环可以更加直观简洁地遍历容器或数组中的元素,而不需要手动管理迭代器或索引变量。这使得代码更易读、易维护,并且减少了出错的可能性。范围for循环也可以与自定义类型一起使用,只要该类型支持迭代操作。因此,范围for功能的添加使得C++代码更加现代化和易用。

四,C++11后,STL容器变化

 前面所学的STL中,现在回望这些C++11才出现的容器。

还有一些比如:右值引用等等,这个我们后面细讲。

五,左值与右值

1. 左值

左值是一个表示数据的表达式(如变量名或解引用的指针), 我们 可以获取它的地址 +可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时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& pvalue = *p;return 0;
}

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

2. 右值

右值也是一个表示数据的表达式,如: 字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等, 右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。
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;
}

3,左值与右值之间的比较

左值 引用总结:
1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右
int main()
{// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a;   // ra为a的别名//int& ra2 = 10;   // 编译失败,因为10是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;return 0;
}
右值引用总结:
1. 右值引用 只能右值,不能引用左值
2. 但是右值引用可以move以后的左值
int main()
{// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 无法从“int”转换为“int &&”// message : 无法将左值绑定到右值引用int a = 10;int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);return 0;
}

4. 右值引用使用场景

聊使用场景前我们需要先更深入的了解右值引用: 右值是不可获取地址的,同时我们在用左值引用时,因为其不可修改性,还必须添加 const 才能接收。那我们思考下面代码

补充: 

因此,我们可以这么理解右值引用是用来修改右值的工具,那什么场景下使用该工具?  请观察下面是简易实现的string类代码:

(1,将死右值的移动语义操作 

namespace bit
{class string{public:typedef char* iterator;iterator begin() {return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){
cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造=====================================string(string&& s):_str(nullptr),_size(0),_capacity(0){cout << "string(string&& s) -- 移动语义" << endl;swap(s);}// 移动赋值=====================================string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动语义" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}

我们用右值引用,从引用中区分出将死右值引用,同时重载赋值函数移动语义,间接达到了减少一次拷贝的功能,解析如下: 

因此,自定义类型的左值经过move库函数操作后, 被转化为了将亡右值后,未来可能出现将亡右值数据被替换,因此move慎用!! 

(2. 编译器传值返回优化

面对局部对象的传值返回的性能消耗,编译器也做了优化,如下:

 当然这种优化跟编译器的版本,新旧有关,我们了解即可。

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?因为:有些场景下,可能真的需要用右值去引用左值实现移动语义。 当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中, std::move()函数位于 头文件中,该函数名字具有迷惑性,它 并不搬移任何东西,唯一的功能就是将一个 左值强制转化为右值引用 ,然后实现移动语义

(3,将亡值做参数

对于参数是 将亡右值的插入接口,C++11就可以用 移动拷贝提高效率。

对于我们使用接口来说,STL 容器插入接口函数也增加了右值引用版本:
http://www.cplusplus.com/reference/list/list/push_back/
http://www.cplusplus.com/reference/vector/vector/push_back/

(4. 完美转发

模板中的&& 万能引用  

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<typename T>
void PerfectForward(T&& t)  // 模板中的&&,叫做万能引用(引用折叠)
// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。                           
{Fun(t);
}int main()
{PerfectForward(10);           // 右值int a;PerfectForward(a);            // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);      // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

但万能引用,却不一定“万能”,模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,不代表就能表示左右值引用类型,引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发。

头文件:utility

用万能引用接受过数据,尽量每次用完美转发的形式重新获取准确数据类型再作为参数。

 5,默认成员函数

原来 C++ 类中,有 6 个默认成员函数:
1. 构造函数
2. 析构函数
3. 拷贝构造函数
4. 拷贝赋值重载
5. 取地址重载
6. const 取地址重载
最后重要的是前 4 个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
1. 没有自己实现 移动构造 函数,且析构函数 、拷贝构造、拷贝赋值重载 都未实现 。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝;自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
2. 没有自己实现 移动赋值重载 函数,且析构函数 、拷贝构造、拷贝赋值重载 都未实现 ,那么编译器会自动生成一个默认移动赋值默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝;自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。( 默认移动赋值跟上面移动构造完全类似)
如果你提供了移动构造 者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

(1. 默认成员函数的强制生成

关键字:  default

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
 class Person{public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p) // 拷贝构造,移动构造+移动赋值无法生成:_name(p._name), _age(p._age){cout << "拷贝构造" << endl;}Person(Person&& p) = default; // 强制生成移动构造Person& operator=(Person&& p) = default; // 移动赋值基于移动构造private:string _name;int _age;};int main(){Person s1("1111", 2);Person s2 = s1;Person s3 = std::move(s1);return 0;}

(2. 默认成员函数的禁止调用生成

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称= delete修饰的函数为删除函数

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

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

相关文章

一篇文章教会你如何在IOS真机上完美运行React Native

一篇文章教会你如何在IOS真机上完美运行React Native 项目初始化项目配置可能遇到的问题没有账号也没有Team设备上没有打开开发者模式&#xff0c;也没有信任开发者证书 无线调试 项目初始化 在终端使用**npx react-native init ProjectName**初始化React Native项目。 进入项…

力扣550 游戏玩法分析 IV

目录 题目描述 思路整理 1. 首次登录日期 2. 第二天登录 3. 计算比率 实现思路 完整代码及解释 题目描述 Table: Activity ----------------------- | Column Name | Type | ----------------------- | player_id | int | | device_id | int | | ev…

117.移除链表元素(力扣)

题目描述 代码解决 class Solution { public:ListNode* removeElements(ListNode* head, int val) {//删除头节点while(head!NULL&&head->valval){ListNode*tmphead;headhead->next;delete tmp;}//删除非头节点ListNode*curhead;while(cur!NULL&&cur-&g…

安卓虚拟机ART和Dalvik

目录 一、JVM和Dalvik1.1 基于栈的虚拟机字节码指令执行过程 1.2 基于寄存器的虚拟机 二、ART与Dalvikdex2aotAndroid N的运作方式 三、总结 一、JVM和Dalvik Android应用程序运行在Dalvik/ART虚拟机&#xff0c;并且每一个应用程序对应有一个单独的Dalvik虚拟机实例。 Dalvik…

Vue.js 实用技巧:深入理解 Vue.set 方法

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

20 个不同的 Python 函数实例

Python 是一种广泛使用的高级编程语言&#xff0c;其函数是 Python 编程中至关重要的概念之一。函数是一段可以重复使用的代码块&#xff0c;可以接收输入参数并返回输出结果。使用函数能够提高代码的可读性、可维护性和重用性。 基础知识 在 Python 中&#xff0c;函数使用关…

【如何在Docker中,修改已经挂载的卷(Volume)】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 提示&#xff1a;添加投票&#xff01;&#xff01;&#xff01; 目录 简述概要知识图谱 简述概要 如何在Docker中&#xff0c;修改已经挂载的卷&#xff08;Volume&#xff09; 知识图谱 在Docker中&#xff0c;修改已经挂载…

本地navicate连接vm虚拟机中的mysql5.7docker容器

一&#xff0c;配置 前提是我已经启动的mysql5.7容器 使用 docker ps -a 查看所有的容器 使用 docker exec -it c4f9 bash 进入mysql命令行&#xff0c;注意这个c4f9是容器唯一id&#xff0c;不用写全连接mysql mysql -uroot -p123456&#xff0c;连接成功后 输入 show datab…

蓝桥杯——123

123 二分等差数列求和前缀和数组 题目分析 连续一段的和我们想到了前缀和&#xff0c;但是这里的l和r的范围为1e12&#xff0c;明显不能用O(n)的时间复杂度去求前缀和。那么我们开始观察序列的特点&#xff0c;可以按照等差数列对序列进行分块。如上图&#xff0c;在求前10个…

当代体育科技期刊投稿邮箱

《当代体育科技》杂志是由国家新闻出版总署批准的正规体育类学术期刊&#xff0c;刊登国内外体育科技领域的新技术、新成果&#xff0c;介绍体育运动项目新的训练方法、动态和发展趋势&#xff0c;宣传科学技术在体育行业中的应用&#xff0c;促进我国体育科技事业的发展。适用…

【笔记版】docker常用指令---systemctl类、docker状态

systemctl [options] docker 启动&#xff1a;system start docker查看状态&#xff1a;systemctl status docker停止&#xff1a;systemctl stop docker有警告&#xff1a;service关闭了&#xff0c;但是docker.socket仍响应解决方法&#xff1a;systemctl stop docker.socket…

springboot2入门到实战-数据库缓存同步框架canal

canal框架 canal [kə’nl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署&#xff0c;存在跨机房同步的业务需求&#xff0c;实现方式主要是基于业务 trig…

【sgCollapseBtn】自定义组件:底部折叠/展开按钮

特性&#xff1a; 支持自定义折叠状态支持自定义标签名称 sgCollapseBtn源码 <template><div :class"$options.name" click"show !show" :placement"placement"><div class"collapse-btns"><div class"c…

嵌入式中volatile关键字的使用方法

Hi,大家好&#xff01; 今天我们来学习一下volatile关键字&#xff0c;volatile关键字想必大家在平时编程中都见过或用过。可是小伙伴们有没有想过什么时候需要使用volatile关键字吗&#xff1f; 在C语言中&#xff0c;volatile是一个关键字&#xff0c;用于告诉编译器不要优化…

Java 打包 SpringBoot 项目报错

Java 打包 SpringBoot 项目报错 问题重现 Please refer to xxxx for the individual test results. Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. 解决问题 在 pom.xml 的 <properties> 中添加项目代码 <s…

QUIC来了!

什么是QUIC QUIC&#xff0c;快速UDP网络连接(Quick UDP Internet Connection)的简称&#xff0c;即RFC文档描述它为一个面向连接的安全通用传输协议。其基于UDP协议实现了可靠传输及拥塞控制&#xff0c;简单来说&#xff0c;QUIC TCP TLS。 为什么有了QUIC HTTP2.0为了为了…

MySQL 的备份方式

MySQL 的备份方式 1. 逻辑备份 逻辑备份是指将数据库中的数据导出为可读的 SQL 语句&#xff0c;通过这些语句可以还原数据库的结构和数据。 使用 mysqldump 工具进行逻辑备份 - **命令示例&#xff1a;**mysqldump -u [username] -p [database_name] > backup_file.sql- …

Spring中的数据校验---JSR303

介绍–什么是JSR303 JSR 303是Java中的一项规范&#xff0c;用于定义在Java应用程序中执行数据校验的元数据模型和API。JSR 303的官方名称是"Bean Validation"&#xff0c;它提供了一种在Java对象级别上执行验证的方式&#xff0c;通常用于确保输入数据的完整性和准…

飞链云共创伙伴亮相温州大学,全国首个AIGC大学俱乐部成立!

在这个充满创新活力的时代&#xff0c;我们见证了一个又一个里程碑式的事件。3月3日&#xff0c;温州大学AIGC俱乐部的成立仪式盛大举行&#xff0c;这标志着全国首个AIGC大学俱乐部的诞生。更让人激动的是&#xff0c;飞链云AI共创伙伴——应流&#xff08;广州&#xff09;科…

PFA晶圆夹在半导体芯片制造中的应用

随着半导体技术的不断进步&#xff0c;晶圆制造作为集成电路产业的核心环节&#xff0c;对生产过程的精密性和洁净度要求日益提高。在众多晶圆制造工具中&#xff0c;PFA&#xff08;全氟烷氧基&#xff09;晶圆夹以其独特的材质和性能&#xff0c;在近年来逐渐受到业界的广泛关…