C++11的部分新特性

目录

1.列表初始化

1.1 { } 初始化

1.2 std::initializer_list

2.声明

2.1 auto

2.2 decltype

2.3 nullptr

3. 范围for

4.STL中的一些变化

5.右值引用与移动语义

5.1 左值引用与右值引用

5.2 左值引用与右值引用的比较

5.3 右值引用使用场景

5.4 完美转发

6.新的类功能

6.1 默认成员函数

6.2 类成员变量初始化

6.3 default

6.4 delete

6.5 final与override

7.可变参数模板

7.1 递归方式展开

7.2 逗号表达式展开


1.列表初始化

1.1 { } 初始化

{}最初是在C++98中被用于初始化数组或者初始化结构体,在C++11中允许用列表来初始化所有内置类型和自定义类型。{}与变量间的=可以省略。

例如:

int main()
{int a = 4;//C++11之前的初始化方式int b = { 4 };// 新初始化方式,这里的 = 可以删除int* c = new int(4);//旧版本int* d = new int{ 4 };//{}也可用于newreturn 0;
}

{}也可用于调用多参数的构造:

class A
{
public:A(int x = 0, char y = '0', double z = 0):_x(x), _y(y), _z(z){}
private:int _x;char _y;double _z;
};
int main()
{A n(1, 'a', 0.5);//旧版本A t{ 1,'a',0.5 };//新版本return 0;
}

1.2 std::initializer_list

std::initializer_list是一种类型,用来访问初始化列表中的值,一般作为构造函数的参数,C++11中STL的一些容器就增加了用std::initializer_list作参数的构造函数。它可以类似看做是用一个数组存储了{ }里面的内容,然后通过取出里面的元素来初始化。

以vector为例:

int main()
{vector<int>v{1,2,3,4,5};return 0;
}

这里vector的构造函数增加了std::initializer_list作参数的构造函数,它存储了{}里面的内容,这个构造函数的内部实现就是取出{}里面的内容,然后再添加到vector里面。

这样初始化容器就更方便了。比如:

int main()
{vector<pair<int, int>>v{ {1,1},{2,2},{3,3} };return 0;
}

这里我们就不用先构造pair再来初始化了,直接用{}代替即可。

2.声明

2.1 auto

C++98中auto是一个存储类型的说明符,表明变量是局部自动储存类型,但局部域中局部变量的定义默认为自动储存类型,所以auto就没什么价值。C++11中,auto被用于变量类型的自动推导,这就要求用auto定义变量时必须显示初始化,编译器会根据初始化的值来自动推导变量类型。

#include<iostream>
using namespace std;
#include<map>
int main()
{auto t = 1;auto n = 1.0;auto m = 'a';map<int,int>x;auto y=x.begin(); cout << typeid(t).name() << endl;cout << typeid(n).name() << endl;cout << typeid(m).name() << endl;cout << typeid(y).name() << endl;return 0;
}

2.2 decltype

decltype用于将变量的类型声明为表达式的指定类型。

#include<iostream>
using namespace std;
int main()
{int a = 1;double b = 1.1;decltype(a * b) c;//c的类型为a*b的类型,即doublecout << typeid(c).name() << endl;decltype(&a) d;//d的类型为&a的类型,即int*cout << typeid(d).name() << endl;return 0;
}

2.3 nullptr

在C++中,NULL被定义为字面常量0,这就意味着会发生矛盾,因为0既能代表空指针,又能代表整形常量0。因此C++11中用nullptr代表空指针。

3. 范围for

在C++98中使用for来遍历需要指明范围。

#include<iostream>
using namespace std;
int main()
{int arr[] = {1,2,3,4,5,6,7,8};for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){cout << arr[i] << " ";}cout << endl;return 0;
}

C++11引入了范围for,当我们需要遍历一个完整的容器时,完全可以让编译器自动去推导出遍历的范围。

#include<iostream>
using namespace std;
int main()
{int arr[] = { 1,2,3,4,5,6,7,8 };//语法为://for(变量声明:容器)//{}//容器arr中的值会依次给到k,每次通过访问k就能访问arr中的元素了for (int k : arr){cout << k << " ";}cout << endl;return 0;
}

如果想要修改容器中的值,可以在上面的k前加上引用&。

注意:范围for实际上只是简化了使用者的代码。而底层依旧会编译成原本需要指出范围的版本。

通过范围for,我们范围STL中的部分容器就更方便了。

#include<iostream>
using namespace std;
#include<vector>
int main()
{vector<int> v{ 1,2,3,4,5,6,7,8 };for (int k : v){cout << k << " ";}cout << endl;return 0;
}

4.STL中的一些变化

STL中增加了一些新容器

<array>Array header(header)
<forward_list>Forward list(header)
<undered_map>Undered map header(header)
<undered_set>Undered set header(header)

同时STL还增加了一些新接口,如cbegin()、cend()等,但最有用的是增加了插入接口函数的右值版本,下面进行讲解。

5.右值引用与移动语义

5.1 左值引用与右值引用

C++原本中就有引用的语法,C++11中增加了右值引用的语法,我们之前的引用用法被称为左值引用,无论是右值引用,还是左值引用,都是给变量取别名。

对于左值(如变量名,或解引用的指针等),我们通常可以获取它的地址,可以对它进行赋值,左值可以出现在赋值符号的左边,而右值不能出现在赋值符号的左边。对于const修饰的左值,虽然不能修改它的值,但可以获取它的地址。

对于右值(如字面常量,函数返回值(不能是左值引用返回),表达式返回值等),不能取地址,只能出现在赋值符号的右边,不能出现在赋值符号的左边。

需要注意的是,对右值进行引用的变量为左值,如上图的ra,rb等,右值本身不可取地址,但对右值进行引用的变量可以取地址。

5.2 左值引用与右值引用的比较

  • 左值引用只能引用左值,不能引用右值
  • const 左值引用即可以引用左值,也可以引用右值
  • 右值引用只能引用右值,不能引用左值
  • 右值引用可以引用move以后的左值

5.3 右值引用使用场景

以下是模拟实现string的部分代码:

namespace bit
{class string{public:string(const char ch = '0'):_str(new char[2] {ch, '\0'}),_size(1),_capacity(1){cout << "string(const char ch )" << endl;}string(const string& t):_str(new char[t._capacity + 1]),_size(t._size),_capacity(t._capacity){cout << "string(const string& t)——拷贝构造" << endl;strcpy(_str, t._str);}string operator=(const string& t){if (&t != this){delete[] _str;_str = new char[t._capacity + 1];_size = t._size;_capacity = t._capacity;strcpy(_str, t._str);return *this;}return *this;}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}~string(){delete[]_str;_str = nullptr;_size = _capacity = 0;}private:char* _str;size_t _size;size_t _capacity;};
}

左值引用的优势:

作参数和返回值可以提高效率。

void func1(bit::string t)
{}
void func2(bit::string& t)
{}
int main()
{bit::string s("hollo");cout << endl;cout << "func1:" << endl;func1(s);cout << endl;cout << "func2:" << endl;func2(s);return 0;
}

比较上图中func1与func2,发现func2没有拷贝构造,提高了效率。

左值引用的短板:

当函数返回值为临时变量时就不能使用左值引用返回,这时接受返回值就必须进行一次拷贝构造。

bit::string func()
{return "hollo";
}
int main()
{bit::string s = func();return 0;
}

这里编译器进行了优化,将两次拷贝构造优化为直接构造。但对于一些没有进行优化的编译器而言,就必须进行两次构造。

这里可以用右值引用解决这个问题。

先提出移动构造的概念:移动构造就是将右值的资源窃取过来占为己用,而不是进行深拷贝。

string(string&& t):_str(nullptr),_size(0),_capacity(0)
{cout << "string(string&& t)——移动构造" << endl;swap(t);
}

这样对于一些将要销毁的变量,我们通过移动构造直接窃取它们的资源,不必进行拷贝,提高了效率。

我们之前提到过右值引用可以引用move过的左值,C++11中函数move()的作用就是将左值变为右值,然后实现移动构造。

int main()
{bit::string t("666");bit::string s = move(t);return 0;
}

但我们通常不会按上述代码使用,因为这就意味着t中的资源被转移了。

5.4 完美转发

模板中的&&万能引用

这里唯一的缺陷就是x始终为左值,如果传入参数为右值,虽然x是右值引用,但它本身仍然是左值。无法再传递过程中保留属性,因此我们就需要使用完美转发来解决这个问题。

std::forward 完美转发在传参的过程中保留对象原生类型属性

void fun(int& x)
{cout << "左值" << endl;
}void fun(int&& x)
{cout << "右值" << endl;
}
template<typename T>
void perfect(T&& x)
{fun(forward<T>(x));
}
int main()
{int a;perfect(a);perfect(move(a));return 0;
}

如果不使用forward,直接使用x,那么结果都是左值。

6.新的类功能

6.1 默认成员函数

C++11中增加了两个默认成员函数,移动构造函数与移动赋值运算符重载。

注意:

  • 如果你没有实现移动构造函数,且没有实现拷贝构造函数,赋值运算符重载,析构函数中的任意一个,那么编译器会自动生成一个移动构造函数,对于内置类型会逐字节拷贝,对于自定义类型会调用它的移动构造函数,如果该类型没有实现移动构造,就调用它的拷贝构造。
  • 如果你没有实现移动赋值运算符重载,且没有实现拷贝构造函数,赋值运算符重载,析构函数中的任意一个,那么编译器会自动生成一个移动赋值运算符重载,对于内置类型会逐字节拷贝,对于自定义类型会调用它的移动赋值运算符重载,如果该类型没有实现移动赋值运算符重载,就调用它的赋值运算符重载。
  • 如果你实现了移动构造或移动赋值,那么编译器就不会生成默认的拷贝构造与拷贝赋值。

6.2 类成员变量初始化

C++11中允许在类定义时给成员变量缺省值,在编译器默认生成的构造函数中会使用缺省值来初始化变量。

6.3 default

default用于生成因为某些原因而没有默认生成的函数。

如果我们实现了拷贝构造,那么编译器就不会输出默认的移动构造,这时我们可以使用default来生成默认的移动构造。

6.4 delete

delete用于禁止生成某些默认生成的函数。

如果我们没有实现拷贝构造,那么编译器会输出一个默认的拷贝构造,这时我们可以使用delete来禁止编译器删除默认的拷贝构造。

6.5 final与override

final用于一个虚函数时,表明该成员函数不能被重写。

override用于一个虚函数时,会检查它是否完成了对父类的某个虚函数的重写。

二者在多态那部分已进行讲解。

7.可变参数模板

C++11 的模板支持可变参数,这里简单的介绍一下。

以下就是一个可变参数的函数模板。

上面的参数arg前面有省略号,所以它是可变模板参数,被称为参数包,它包含1~n个模板参数,我们不能直接获取它的每个参数,必须展开参数包才能获取。

7.1 递归方式展开

#include<iostream>
using namespace std;
//终止函数递归
void print()
{}
template<class T,class...Args>
void print(T x, Args... arg)
{cout << x << " ";//以递归的方式展开参数包print(arg...);
}
template<class ...Args>
void func(Args... arg)
{print(arg...);
}
int main()
{func('a', 5, 4, 'c', 6.5, 4, 2);return 0;
}

这里我们每次取出一个模板参数,然后剩下的参数包进行递归,当参数包为空时,就不会走当前递归的函数,而是走另一个重载的函数,以此来结束。

7.2 逗号表达式展开

#include<iostream>
using namespace std;
template<class T>
void print(T x)
{cout << x << " ";
}
template<class ...Args>
void func(Args... arg)
{int arr[] = { (print(arg),0)... };
}
int main()
{func('a', 5, 4, 'c', 6.5, 4, 2);return 0;
}

这里我们利用C++11列表初始化的特性,通过列表来初始化变长数组,这样{(print(arg),0)...}就展开成{(print(arg1),0),(print(arg2),0),(print(arg3),0),...,(print(argn),0)},逗号表达式的意义在于调用print函数后,以0作为整个表达式的结果,刚好作为arr数组的一个元素。

C++11STL中新出的成员函数emplace_back就支持可变参数,它与push_back的区别就在于它有些情况能用参数直接进行构造,而不是走拷贝构造或者移动构造,故而在一些情况下比push_back更高效。

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

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

相关文章

JavaScript事件处理和常用对象

文章目录 前言一、事件处理程序 1.JavaScript 常用事件2.事件处理程序的调用二、常用对象 1.Window 对象2.String 对象3.Date 对象总结 前言 JavaScript 语言是事件驱动型的。这意味着&#xff0c;该门语言可以通过事件触发来调用某一函数或者一段代码。该文还简单介绍了Window…

Android 11 FileProvider的使用和限制

概述&#xff1a; 从Android 7开始&#xff0c;将不允许在app之间&#xff0c;使用file uri,即file://的方式&#xff0c;传递一个file&#xff0c;否则会抛出异常&#xff1a;FileUriExposedException &#xff0c;其解决方案&#xff0c;就是使用FileProvider&#xff0c;用c…

有关 Element-ui 的一些思考

本篇文章是基于 element-ui 的 主题样式修改 曾经为了修改组件风格,一个项目用了上百个样式穿透,后来才发现定制一个主题就够了! 第一步,在官网的主题页面,修改背景色、字体颜色及边框颜色 第二步,下载主题 第三步,用下载的css文件替换掉默认的css文件

如何让Windows控制台窗口不接受鼠标点击(禁用鼠标输入)

一、简述 在我们编写控制台应用程序时&#xff0c;默认情况下程序的打印输出会在控制台窗口中进行显示&#xff0c;我们在写服务功能时在窗口中会不断打印消息输出&#xff0c;这个时候如果使用鼠标点击了控制台窗口&#xff0c;会阻塞程序的继续运行&#xff0c;导致我们的程…

SpringBoot集成Thymeleaf模板引擎,为什么使用(详细介绍)

学习本技术第一件事&#xff1a;你为什么要使用&#xff0c;解决什么问题的&#xff1f; 1.为什么使用&#xff08;使用背景&#xff09;&#xff1f; 首先应用场景是单体项目&#xff0c;如果是前后端分离就不用关注这个了&#xff0c;因为单体项目你前后端都是写在一个项目…

性能小白终于能看懂Jmeter报告了

对于刚接触性能测试的初学者来说&#xff0c;分析JMeter生成的测试报告无疑是一个巨大的挑战。面对大量的数据信息&#xff0c;如何快速理解响应时间、吞吐量、错误率等关键指标&#xff0c;往往让人感到困惑。今天&#xff0c;让我们一起探讨如何轻松看懂JMeter的性能测试报告…

多线程篇(其它容器- CopyOnWriteArrayList)(持续更新迭代)

一、CopyOnWriteArrayList&#xff08;一&#xff09; 1. 简介 并发包中的并发List只有CopyOnWriteArrayList。 CopyOnWriteArrayList是一个线程安全的ArrayList&#xff0c;对其进行的修改操作都是在底层的一个复制的数 组&#xff08;快照&#xff09;上进行的&#xff0…

基于STM32C8T6的CubeMX:HAL库点亮LED

三个可能的问题和解决方法&#xff1a; 大家完成之后回来看&#xff0c;每一种改错误都是一种成长&#xff0c;不要畏惧&#xff0c;要快乐&#xff0c;积极面对&#xff0c;要耐心对待 STMCuBeMX新建项目的两种匪夷所思的问题https://mp.csdn.net/mp_blog/creation/editor/1…

【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

在安卓源码的设计中&#xff0c;将将屏幕分为了37层&#xff0c;不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析&#xff0c;整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

前端技术(七)——less 教程

一、less简介 1. less是什么&#xff1f; less是一种动态样式语言&#xff0c;属于css预处理器的范畴&#xff0c;它扩展了CSS语言&#xff0c;增加了变量、Mixin、函数等特性&#xff0c;使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 &#xff0c;也可以借助Node.js在服…

Semaphore UI --Ansible webui

1、安装python python下载地址 https://www.python.org/downloads/ 选好版本下载 wget https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz安装编译工具 sudo dnf groupinstall "Development Tools"安装依赖包 dnf install bzip2-devel ncurses-deve…

IDEA 常用配置和开发插件

件市场中搜索并安装“Git Integration”插件。 一、前言 在本篇文章中我会为大家总结一些我自己常用的配置和开发插件&#xff0c;此外也给大家提供一个建议&#xff0c;可以根据自己的项目需求和个人偏好选择适合的插件。另外&#xff0c;IDEA 也在不断更新&#xff0c;可能会…

哈希表、算法

哈希表 hash&#xff1a; 在编程和数据结构中&#xff0c;"hash" 通常指的是哈希函数&#xff0c;它是一种算法&#xff0c;用于将数据&#xff08;通常是字符 串&#xff09;映射到一个固定大小的数字&#xff08;哈希值&#xff09;。哈希函数在哈希表中尤为重要…

使用vue2+axios+chart.js画折线图 ,出现 RangeError: Maximum call stack size exceeded 错误

目录 效果图 解决方案 修正要点 效果图 修改前App.vue代码&#xff1a; <template><div id"app"><canvas id"myChart"></canvas></div> </template><script> import axios from axios; import { Chart, regis…

stm32 W25Q数据存储

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、cubemx配置二、keil中文件修改与配置三、几个重要函数的说明四、DMA方式传输&#xff08;待写&#xff09;总结 前言 W25Q128 容量为128位 128/8 16 也就…

Mac 上终端使用 MySql 记录

文章目录 下载安装终端进入 MySql常用操作查看数据库选择一个数据库查看当前选择的数据库Navcat 打开提示报错参考文章 下载安装 先下载社区版的 MySql 安装的过程需要设置 root 的密码&#xff0c;这个是要进入数据库所设定的&#xff0c;所以要记住 终端进入 MySql 首先输…

Linux基础-Makefile的编写、以及编写第一个Linux程序:进度条(模拟在 方便下载的同时,更新图形化界面)

目录 一、Linux项目自动化构建工具-make/Makefile ​编辑 背景&#xff1a; makefile小技巧&#xff1a; 二、Linux第一个小程序&#xff0d;进度条 先导&#xff1a; 1.如何利用/r,fflush(stdout)来实现我们想要的效果&#xff1b; 2.写一个倒计时&#xff1a; 进度条…

智能家居环境监测系统设计(论文+源码)

1. 系统方案 系统由9个部分构成&#xff0c;分别是电源模块、烟雾传感器模块、GSM发送短信模块、报警模块、温度传感器模块、人体红外感应模块、按键设置模块、显示模块、MCU模块。各模块的作用如下&#xff1a;电源模块为系统提供电力&#xff1b;烟雾传感器模块检测烟雾浓度&…

猫狗识别大模型——基于python语言

目录 1.猫狗识别 2.数据集介绍 3.猫狗识别核心原理 4.程序思路 4.1数据文件框架 4.2 训练模型 4.3 模型使用 4.4 识别结果 5.总结 1.猫狗识别 人可以直接分辨出图片里的动物是猫还是狗&#xff0c;但是电脑不可以&#xff0c;要想让电脑也分辨出图片里的动物是猫还是小…

C++面试3

一、常用设计模式 https://blog.csdn.net/m0_71530237/article/details/141140118?spm1001.2014.3001.5501 二、死锁以及解决方式&#xff1f; 死锁&#xff1a;一种常见的并发问题&#xff0c;发生在多个进程或线程因为竞争资源而陷入相互等待的状态&#xff0c;导致这些进…