C++11的部分新特性

news2024/9/21 16:25:04

目录

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 };//{}也可用于new
	return 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的类型,即double
	cout << 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.coloradmin.cn/o/2139312.html

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

相关文章

操作系统week2

操作系统学习 二.处理机管理 19.生产者-消费者问题 问题&#xff1a; 代码&#xff1a; 20.多生产者-多消费者问题 实现&#xff1a; 21.吸烟者问题(单生产者-多消费者) 问题&#xff1a; 实现&#xff1a; 22.读者-写者问题 问题&#xff1a; 读优先的代码&…

CentOS7更换阿里云yum更新源

目前CentOS内置的更新安装源经常报错无法更新&#xff0c;或者速度不够理想&#xff0c;这个时候更换国内的镜像源就是一个不错的选择。 备份内置更新源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载阿里云repo源&#xff08;需要系统…

Cubieboard2(三) 系统构建 —— WSL Ubuntu 中挂载 U 盘(SDCard)

文章目录 1 WSL Ubuntu 中挂载 U 盘(SDCard)2 usbipd 搭建虚拟机与宿主机 USB 通信桥梁3 WSL 内核添加 USB 设备驱动3.1 编译 WSL Linux 内核3.2 挂载 USB(SDCard) 设备 附录&#xff1a;WSL 操作命令附录&#xff1a;git 仓库检出 1 WSL Ubuntu 中挂载 U 盘(SDCard) Linux 驱动…

使用OpenFeign在不同微服务之间传递用户信息时失败

文章目录 起因原因解决方法&#xff1a; 起因 从pay-service中实现下单时&#xff0c;会调用到user-service中的扣减余额。 因此这里需要在不同微服务之间传递用户信息。 但是user-service中始终从始至终拿不到user的信息。 原因 在pay-service中&#xff0c;不仅要Enable O…

YOLO学习笔记 | YOLO目标检测算法(YOLO-V2)

github&#xff1a;https://github.com/MichaelBeechan CSDN&#xff1a;https://blog.csdn.net/u011344545 YOLO-V2 V1与V2区别Batch Normalization更大分辨率YOLO-V2网络结构 V1与V2区别 V2更强更快 Batch Normalization 更大分辨率 YOLO-V2网络结构

顺序栈讲解

文章目录 &#x1f34a;自我介绍&#x1f34a;顺序栈讲解生活中的例子栈的基本概念入栈和出栈 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#xff0c;我是小…

《ImageNet Classification with Deep Convolutional Neural Networks》论文导读

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl《ImageNet Classification with Deep Convolutional Neural Networks》是一篇在深度学习领域具有重要影响力的论文,由Alex Krizhevsky、Ilya Sutskever和Geoffrey E. Hinton等人撰写。该论文主要…

窗口嵌入桌面背景层(vb.net,高考倒计时特供版)

开发思路 根据系统生成高考倒计时的具体时间&#xff0c;附加江苏省省统考的时间生成算法&#xff0c;并且用户可以根据实际情况调整前后30天&#xff0c;具有丰富多彩的图片库和强大的自定义功能&#xff0c;效果图见P3 目前程序处于正式版的1.4版本&#xff0c;本程序由本作…

【信创】Linux上图形化多ping工具--gping的编译安装与打包 _ 统信 _ 麒麟 _ 方德

原文链接&#xff1a;【信创】图形化多ping工具gping的编译安装与打包 | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在Linux操作系统上使用gping的文章。gping是一款非常实用的命令行工具&#xff0c;它将传统的ping命令进行了可视化改进…

CCPC赛后补题-线性基

模板题&#xff1a;https://www.luogu.com.cn/problem/P3812 线性基可以用一个长度为$ \log_2N $的数组描述值域[1,N]&#xff0c;0的情况需要特判。 一个长度为64的线性基可以描述所有的64位整数。 在2024年CCPC网络赛中&#xff0c;考到了线性基。没学过&#xff0c;追悔莫…

解决iframe嵌套第三方页面被拒绝

背景 很多时候&#xff0c;出于安全考虑&#xff0c;没有第三方页面的允许&#xff0c;我们是无法直接通过iframe去打开别人的第三方页面的&#xff0c;通常他们会通过在页面请求的响应头增加X-Frame-Options (去了解)和Content-Security-Policy (去了解)。 目的 可是有些时…

详细介绍 Servlet 基本概念——以餐厅服务员为喻

什么是Servlet Servlet 是 Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;中用于构建动态 web 应用的组件。它在服务器端运行&#xff0c;并能够处理客户端请求&#xff0c;生成动态响应。以下是对 Servlet 的详细介绍&#xff1a; 1. Servlet 的基本概念…

COSMOSPANDA星际熊猫助阵长江商学院高尔夫周年庆典

在金秋送爽的美好时节&#xff0c;星际漫游&#xff08;广州&#xff09;品牌管理有限公司旗下备受欢迎的潮玩IP“COSMOSPANDA星际熊猫”与长江商学院深圳校友会强强联手&#xff0c;于9月10日在风景如画的中山雅居乐长江高尔夫球会成功举办了“长江商学院深圳校友会高尔夫球队…

基于ssm+vue+uniapp的智能停车场管理系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

6.1 溪降技术:绳结

目录 6.1 绳结电子书&#xff1a;绳结1级概览正确打结打绳结绳结组成部分学习术语八字套结&#xff08;双八字结&#xff09;观看技术步骤双重单结&#xff08;反手结绳耳&#xff09;观看技术步骤骡子结&#xff08;驮马结&#xff09;观看技术步骤 6.1 绳结 电子书&#xff1…

【人工智能】AI创业的前沿思考 | 从垂直领域到通用智能模型AGI的崛起

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

影视剧数据分析与可视化

数据入口&#xff1a;影视剧数据集 - Heywhale.com 读取的数据包含750条条目&#xff0c;分布在6个列中。以下是数据的列和它们的数据类型&#xff1a; 影视剧名称&#xff08;object&#xff09;&#xff1a;表示影视剧的名称。影视剧标签&#xff08;object&#xff09;&…

<<编码>> 第 12 章 二进制加法器--全加器 示例电路

全加器的内部构成 info::操作说明 鼠标单击逻辑输入切换 0|1 状态 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/code-hlchs-examples/assets/circuit/code-hlchs-ch12-05-full-adder-internal.txt 全加器 info::操作说明 鼠标…

机器学习实战21-基于XGBoost算法实现糖尿病数据集的分类预测模型及应用

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战21-基于XGBoost算法实现糖尿病数据集的分类预测模型及应用。首先阐述了 XGBoost 算法的数学原理及公式&#xff0c;为模型构建提供理论基础。接着利用 kaggle 平台的糖尿病数据集&#xff0c;通过详细的…

【C++】queue和priority_queue

个人主页~ queue和priority_queue 一、queue的介绍和使用1、queue的介绍2、queue的使用3、queue的模拟实现 二、priority_queue的介绍和使用1、priority_queue的介绍2、priority_queue的使用3、priority_queue的模拟实现 三、仿函数1、仿函数的特征2、仿函数的使用 ex、有关于l…