【C++】C++11特性(上)

✨✨欢迎大家来到Celia的博客✨✨

🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉

所属专栏:C++

个人主页:Celia's blog~

目录

一、列表初始化

二、std::initializer_list

三、右值引用和移动语义

3.1 左值和右值 

3.2  左值引用和右值引用

 3.3 引用延长生命周期

 3.4 左值和右值的参数匹配

3.5 右值引用和移动语义的使用场景

3.5.1 左值引用的使用场景

 3.5.2 移动构造和移动赋值

3.5.3 右值引用、移动语义解决传值返回问题

3.6 引用折叠

3.6.1 引用折叠规则

3.6.2 万能引用

3.7 完美转发

四、可变参数模板

4.1 语法和原理

4.2 包的扩展


一、列表初始化

在C++98中,数组和结构体是可以用{}初始化的:

#include<iostream>
using namespace std;

struct Point
{
	int _x;
	int _y;
};
int main()
{
	Point p{ 1,2 };
	int arr[] = { 1,2,3 };
	int arr[4] = { 5,6,7,8 };
	int arr[8] = { 7,8,9 };
	return 0;
}
  • 在C++11中,为了统一初始化的方式,出现了列表初始化的新初始化方式。
  • 所有类型都可以用{}来初始化(包括内置类型、自定义类型),也叫列表初始化。
  • 自定义类型的初始化本质上是类型转换,编译器会将其优化成直接构造
  • 列表初始化可以省略掉=。
  • 在容器的push/insert操作时,利用列表初始化来构造自定义类型会很方便。
#include<iostream>
using namespace std;

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()
{
	int a{ 1 };
	double b{ 2.14 };
	Date d1 = { 2000,1,1 };
	Date d2{ 2024,10,11 };
	return 0;
}

二、std::initializer_list

  • 有了列表初始化之后,对于自定义类型的初始化方便了许多。但是对于一些容器来说,初始化仍然不是那么方便。比如vector,如果我们想初始化三个不同元素的容器,就需要手动写带有三个参数的构造。那么如果想初始化五个元素,十个元素呢?由此可见,增加构造函数并不是有效的方法。
  • 为了解决这个问题,C++11中引入了一个std::initializer_list的类。这个类的底层是开辟一个数组,并将数据拷贝过来。这个类中有两个指针,分别指向数组的开头和结尾。故这个类支持迭代器访问。
  • 如果一个容器支持参数为std::initializer_list构造方法,那么就可以把std::initializer_list中的元素一个一个增添到指定的容器中,这样初始化就方便了许多。
  • 需要注意的是,std::initializer_list中的元素必须是同一类型的。
int main()
{
	//std::initializer_list构造
	vector<int> v1({1, 4, 5, 6, 7, 8, 9}); //直接构造
	vector<int> v2 = { 1,2,3,4,5,6,7 }; //构造临时对象 + 拷贝构造 → 优化成直接构造
	const vector<int>& v3 = { 1,2,3,4,5 }; //std::initializer_list为临时对象,需要用const引用
	return 0;
}

三、右值引用和移动语义

在C++98中就有了引用的概念,在C++11中新增了右值引用,之前所学的引用叫做左值引用。无论是左值引用还是右值引用,都是给指定对象起别名

3.1 左值和右值 

  • 左值是一个表示数据的表达式,一般是持久状态,储存在内存中,我们可以获取它的地址。例如:变量名、解引用的指针等。被const修饰的左值,不能够二次赋值,但是可以取它的地址。左值可以在等号的左边,也可以在等号的右边。
  • 右值是一个表示数据的表达式,但一般为字面值常量、或者运算过程中产生的临时对象。右值可以在等号的右边,但是不能在等号的左边。右值不可以取地址
  • 左值的英文简写为lvalue,右值的英文简写为rvalue。传统认为,它们是left value和right value的缩写。但是现代认为,lvalue为loactor value的缩写,意为存储在内存中、有明确存储地址可以取地址的对象。rvalue为read value的缩写,指的是那些可以提供数据值,但是不可以寻址的字面常量或临时对象。总的来说,左值和右值的核心区别在于是否可以取地址
int main()
{
	//左值
	int x = 0;
	cout << &x << endl;
	int y = 1;
	//右值
	x + y;
	10;
	"111";
	//cout << &10 << endl;  //error
	return 0;
}

3.2  左值引用和右值引用

  • 左值是给左值取别名,右值是给右值取别名。
  • 左值引用无法直接引用右值,但被const修饰的左值引用可以引用右值
  • 右值引用无法直接引用左值,但是可以引用被move过后的左值
  • move是库里面的一个函数模板,本质内部是进行强制类型转换。
  • 变量表达式都是左值属性,也就是说,当一个右值被右值引用绑定后,这个右值引用的属性是左值
  • 当一个常量被右值引用引用后,可以通过右值引用来修改常量的值。并可以再次使用右值引用来访问它。
void func1(int&& x)
{
	cout << "右值引用" << endl;
}
void func1(int& x)
{
	cout << "左值引用" << endl;
}

int main()
{
	int a = 10;
	int& x1 = a; //左值
	int&& x2 = 1 + 2; //右值
	func1(x2);  //属性是左值
	func1(100); //属性是右值

	cout << x1 << " " << x2 << endl;
	return 0;
}

 3.3 引用延长生命周期

一般来讲,临时对象的生命周期只在当前一行。但若是引用它,临时变量的生命周期就与这个引用的生命周期相同。

int main()
{
	cout << 1 + 2 << endl; //生命周期只在当前一行
	int&& x1 = 2 + 3; //生命周期延长
	const int& x2 = 3 + 4;  //生命周期延长
	cout << x1 << " " << x2 << endl;
	return 0;
}

 3.4 左值和右值的参数匹配

在C++98中,拷贝构造函数的参数为const修饰的左值引用,左值可以匹配,右值也可以匹配。引入右值引用参数后,传入左值会匹配左值引用的构造函数,传入右值会匹配右值引用的构造函数。也就是说,左值引用和右值引用作为参数的函数可以构成函数重载

//函数重载场景示例
void func1(int&& x)
{
	cout << "右值引用" << endl;
}
void func1(int& x)
{
	cout << "左值引用" << endl;
}

3.5 右值引用和移动语义的使用场景

3.5.1 左值引用的使用场景

左值引用的使用场景通常是引用传参或引用作为函数返回值来达到减少拷贝的目的。这已经解决了绝大部分的场景问题。但是还有一些场景是左值引用解决不了的。

//场景1
class Solution {
public:

	string addStrings(string num1, string num2) {
		string str;
		int end1 = num1.size() - 1, end2 = num2.size() - 1;

		int next = 0;
		while (end1 >= 0 || end2 >= 0)
		{
			int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
			int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
			int ret = val1 + val2 + next;
			next = ret / 10;
			ret = ret % 10;
			str += ('0' + ret);
		} 
		if(next == 1)
			str += '1';
		reverse(str.begin(), str.end());
		return str;
	}
};
  • 场景1:
  • 当前场景的返回值不能够使用引用来返回,原因是str是一个在函数内部的局部变量,当函数执行完成后,函数栈帧被销毁,str也就不存在了。尽管引用也有延长生命周期的作用,但仍然改变不了函数栈帧销毁的事实。 
  • C++98在这种场景下只能选择传值返回。
//场景2
class Solution {
public:
	vector<vector<int>> generate(int numRows) 
	{
		vector<vector<int>> vv(numRows);
		for (int i = 0; i < numRows; ++i)
		{
			vv[i].resize(i + 1, 1);
		} 
		for (int i = 2; i < numRows; ++i)
		{
			for (int j = 1; j < i; ++j)
			{
				vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
			}
		} 
		return vv;
	}
};
  •  场景2:
  • 但如果是场景2的情况,传值返回将是一笔巨大的开销,代价会很大很大。

 3.5.2 移动构造和移动赋值

移动构造和拷贝构造很相似,唯一的不同在于:

  • 移动构造的参数是右值引用,拷贝构造的参数是当前类型的左值引用

移动赋值和赋值重载的区别:

  • 移动赋值的参数是右值引用,赋值重载的参数为当前类型的左值引用

对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类,移动构造和移动赋值才有意义,因为移动构造和移动赋值的第⼀个参数都是右值引用的类型,他的本质是要“窃取”引用的右值对象的资源,而不是像拷贝构造和拷贝赋值那样去拷贝资源,从而提高效率。

//string类部分代码举例
string(const string& s)
	:_str(nullptr)
{
	reserve(s._capacity);
	for (auto ch : s)
	{
		push_back(ch);
	}
} 
//移动构造
string(string && s)
{
	swap(s);
}
string& operator=(const string& s)
{
	if (this != &s)
	{
		_str[0] = '\0';
		_size = 0;
		reserve(s._capacity);
		for (auto ch : s)
		{
			push_back(ch);
		}
	} 
	return* this;
} 
// 移动赋值
string& operator=(string && s)
{
	swap(s);
	return *this;
}

3.5.3 右值引用、移动语义解决传值返回问题

移动语义(Move Semantics)是 C++11 引入的一项重要特性,它允许对象的资源(如堆上分配的内存)在不进行深度复制的情况下进行转移。通过移动语义,可以将对象的资源从一个对象转移到另一个对象,从而避免不必要的内存拷贝,提高程序性能和效率。

在之前的代码中不难观察到,移动赋值、移动构造的独特之处在于:直接交换了右值引用对象和当前对象的数据。由于右值引用的对象是右值,比如常量字符串、匿名对象、临时变量等。这些右值原本的生命周期只有一行,在其他代码段也没有机会使用这些右值,故可以直接掠夺它们的资源,也就是把它们的资源交换过来。

string addStrings(string num1, string num2) {
		string str;
		int end1 = num1.size() - 1, end2 = num2.size() - 1;

		int next = 0;
		while (end1 >= 0 || end2 >= 0)
		{
			int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
			int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
			int ret = val1 + val2 + next;
			next = ret / 10;
			ret = ret % 10;
			str += ('0' + ret);
		} 
		if(next == 1)
			str += '1';
		reverse(str.begin(), str.end());
		return str;
	}

这段代码在返回str时,返回值会被当做右值来处理。故该返回值的接收必然会调用移动赋值/移动构造。这会直接导致资源的交换,对于string类来说,交换只不过是一个指针和一些其他成员变量。相比于之前的值拷贝,需要一个一个的拷贝指针指向的资源(比如遍历数组元素),移动赋值/移动构造的效率要高很多很多。这就很好的解决了拷贝时间开销巨大的问题。

3.6 引用折叠

在C++中,不可以直接定义引用的引用,如:

int& && a;  //右值引用的引用?

但如果是模板参数,则可能出现以下情况:

template<class T>
void func(T& data)
{
	cout << data << endl;
}
int main()
{
	int a = 1;
	func<int&&>(a);
	return 0;
}

这种情况下,实例化函数的模板参数为int&&,所以T为int&&,函数参数理论上为int&& &data。这种情况下,参数可以被认为是引用的引用吗?答案是不可以,参数会被折叠成左值引用。这里就涉及到引用折叠的问题。

3.6.1 引用折叠规则

  • 右值引用的右值引用折叠成右值引用,其他所有情况均会折叠成左值引用
template<class T>
void func(T&& data)
{
	cout << data << endl;
}
int main()
{
	int a = 1;
	func<int>(a); //未折叠 参数为int&& //error
	func<int>(0); //未折叠 参数为int&&
	func<int&>(a); //折叠 参数为int&
	func<int&>(0); //折叠 参数为int&  //error
	func<const int&>(a); //折叠 参数为const int&
	func<const int&>(0); //折叠 参数为const int&
	func<int&&>(a); //折叠 参数为int&& //error
	func<int&&>(0); //折叠 参数为int&&

	return 0;
}

3.6.2 万能引用

在了解引用折叠的规则后,我们会发现:

  • 如果函数模板参数给定T类型的右值引用(T&&),那么传入右值引用时,会折叠成右值引用;传入左值引用时,会折叠成左值引用

这种写法的结果与实际传入的参数结果相同,这种写法也叫做万能引用

3.7 完美转发

  • func(T&& t)函数模板中,传左值实例化以后是左值引用的func函数,传右值实例化以后是右值引用的func函数。
  • 右值引用的属性是左值,也就是说,当一个右值引用绑定之后,右值引用变量表达式的属性是左值。接下来传入该引用的属性都会被识别成左值。
  • 如果想保持其原有的右值属性,就需要用到完美转发
  • template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
  • 完美转发实际上也是通过函数模板的引用折叠来实现的。
void func1(int&& x)
{
	cout << "右值引用" << endl;
}
void func1(int& x)
{
	cout << "左值引用" << endl;
}
int main()
{
	int&& a = 0;     //假设这是折叠后的类型,折叠后类型为int&&
	func1(a);
	func1(forward<int>(a));
//template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
//大致原理:
//假设T为int,说明没有折叠/折叠后为int&&,说明期望a是右值,将a强转为右值。
//若T为int&,说明折叠后为int&,说明期望a是左值,将a强转为左值。
	return 0;             
}

四、可变参数模板

4.1 语法和原理

C++11支持可变参数模板,也就是支持可变参数数量函数模板和类模板。可变数目的参数被称作参数包。共有两种参数包:

  • 函数参数包,表示零或多个函数参数。
  • 模板参数包,表示零或多个模板参数。
  • 我们用省略号来指出一个模板参数或函数参数的一个参数包,在模板参数列表中,class...或typename...指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟...指出接下来表示零或多个形参对象列表;函数参数包可以用左值引用或右值引用表示,跟前面普通模板一样,每个参数实例化时遵循引用折叠规则。
  • 可变参数模板的原理跟模板类似,本质还是去实例化对应类型和个数的多个函数。
  • 这⾥我们可以使用sizeof...运算符去计算参数包中参数的个数。
template<class ...Args>
void func2(Args&&... args)
{
	cout << sizeof...(args) << endl;
}
int main()
{
	func2(1, 1.1, "123"); //包中有三个参数
	return 0;
}

4.2 包的扩展

对于参数包,除了能计算参数的个数,更重要的是能使用包中的不同类型的参数。扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。
扩展方式之一如下:

void show()
{
	//最后一个调用传入空参数,需要准备空参数的函数
	cout << endl;
}
template<class T, class ...Args>
void show(T x, Args ...args)
{
	cout << x << " ";
	//第一个参数传给x,剩下的N - 1个参数传给参数包
	//递归调用
	show(args...);
}
template<class ...Args>
void func2(Args&&... args)
{
	cout << sizeof...(args) << endl; //输出参数个数
	//args是N个参数的参数包
	//第一个参数传给x,剩下的N - 1个参数传给参数包
	show(args...);
}
int main()
{
	func2(1, 1.1, string("hello"));
	return 0;
}

实际上,编译器在编译时会自动推导以下函数(同时也是递归的执行过程):

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux(光速安装+ubuntu镜像 serve live-serve desktop)

ubuntu镜像_ubuntu下载地址_ubuntu安装教程-阿里巴巴开源镜像站 Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 清华大学的镜像好一点速度比较快&#xff01; 下载镜像 都是推荐使用服务器版&#xff0c;桌面版一般自己用 amd64 就…

Linux命令详解,全网最详细,看这一篇就够了

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

机器情绪及抑郁症算法

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月12日17点02分 点击开启你的论文编程之旅https://www.aspiringcode.com/content?id17230869054974 计算机来理解你的情绪&a…

美食网的设计与实现

摘 要 随着科技的发展、生活水平的提升&#xff0c;人们更加注重饮食搭配和饮食健康。通过网络技术来加强美食与健康知识的普及是当前一种可行的措施。通过网页浏览美食网&#xff0c;不仅可以普及每道美食的做法&#xff0c;通过制作美食来缓解心情&#xff0c;还可以通过美…

Ubuntu[无桌面]——修改Docker镜像源文件

下载镜像的时候&#xff0c;一般有两种方式&#xff1a; &#xff08;1&#xff09;在宿主主机配置相应的文件/etc/docker/daemon.json&#xff0c;配置镜像源环境地址 &#xff08;2&#xff09;进入https://quay.io/search中&#xff0c;输入搜索需要下载的镜像名称&#xff…

ODOO学习笔记(8):模块化架构的优势

灵活性与可定制性 业务流程适配&#xff1a;企业的业务流程往往因行业、规模和管理方式等因素而各不相同。Odoo的模块化架构允许企业根据自身的具体业务流程&#xff0c;选择和组合不同的模块。例如&#xff0c;一家制造企业可以启用采购、库存、生产和销售模块&#xff0c;并通…

Git的使用(基础语句)

首先如果想要使用git的各项功能&#xff0c;我们要下载Git-2.40.1-64-bit.exe这个驱动程序&#xff0c;并安装它&#xff0c;这个资源我没有办法上传是因为有的博主已经上传过了&#xff0c;所以有VIP的或者有钱哥可以去csdn上自行下载&#xff0c;实在不行加我qq我发你4925396…

labview用sql server数据库存取数据到一个单元格

最近有一个项目上需要一个庞大的数据量&#xff0c;需要很多列&#xff0c;但是百度查了一下sqi server最多支持1024列&#xff0c;这一限制适用于大多数表类型&#xff0c;包括常规表&#xff0c;临时表和表变量&#xff0c;要注意的是如果超出这一限制可能会导致数据的完整性…

Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)

作者&#xff1a;来自 Elastic Benjamin Trent Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)。 嵌入模型输出 float32 向量&#xff0c;通常对于高效处理和实际应用来说太大。Elasticsearch 支持 int8 标量量化&#xff0c;以减小向量大小&#xff0c;同时保持性能。其他…

库打包工具 rollup

库打包工具 rollup 摘要 **概念&#xff1a;**rollup是一个模块化的打包工具 注&#xff1a;实际应用中&#xff0c;rollup更多是一个库打包工具 与Webpack的区别&#xff1a; 文件处理&#xff1a; rollup 更多专注于 JS 代码&#xff0c;并针对 ES Module 进行打包webpa…

2024中国游戏出海情况

01 哪里出海更花钱&#xff1f; 报告显示&#xff0c;中国手游在全球不同市场的获客成本不同&#xff0c;整体来看北美市场竞争更加激烈&#xff0c;其安卓和iOS获客成本是拉丁美洲的12倍和7倍。 按具体市场划分&#xff0c;获客成本最高的TOP 3为韩国、美国和日本&#xff0c…

【达梦数据库】MYSQL迁移到DM字符集转换问题-UTF8mb4|转UTF8(UTF8mb3)

目录 背景现象问题原因原因1&#xff1a;字符集不同原因2&#xff1a;以字节为单位 解决办法方法1&#xff1a;扩大长度 结果验证MYSQLDTSDM 背景 迁移过程环境信息如下&#xff1a; 数据库版本字符集补充MYSQL8.0.xxUTF8mb4DM8.1.3.162UTF8&#xff08;UTF8mb3的简称&#x…

Qt_day10_程序打包(完结)

目录 1. 设置图标 2. Debug和Release版本 3. 动态链接库 4. 打包 5. 联系项目要求 Qt开发的程序最终都是要给用户使用的&#xff0c;用户的电脑上不可能装一个Qt的开发环境导入项目使用。因此项目项目开发完成后需要打包——制作成安装包&#xff0c;用户直接下载并安装即可使用…

RT-DETR融合[ECCV2024]自调制特征聚合SMFA模块及相关改进思路

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《SMFANet: A Lightweight Self-Modulation Feature Aggregation Network for Efficient Image Super-Resolution》 一、 模块介绍 论文链接&#xff1a;https://link.springer.…

postman变量和脚本功能介绍

1、基本概念——global、collection、environment 在postman中&#xff0c;为了更好的管理各类变量、测试环境以及脚本等&#xff0c;创建了一些概念&#xff0c;包括&#xff1a;globals、collection、environment。其实在postman中&#xff0c;最上层还有一个Workspaces的概…

计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议

文章目录 一、TCP/IP五层模型&#xff08;重要&#xff09;二、应用层常见的协议三、TCP与UDP3.1 TCP、UDP的区别&#xff08;重要&#xff09;3.2 运行于TCP、UDP上的协议3.3 TCP的三次握手、四次挥手3.3.1 TCP的三次握手3.3.2 TCP的四次挥手3.3.3 随机生成序列号的原因 四、T…

约束(MYSQL)

not null&#xff08;非空&#xff09; unique&#xff08;唯一&#xff09; default&#xff08;默认约束&#xff0c;规定值&#xff09; 主键约束primary key&#xff08;非空且唯一&#xff09; auto_increment&#xff08;自增类型&#xff09; 复合主键 check&#xff08…

Cent OS-7的Apache服务配置

WWW是什么&#xff1f; WWW&#xff08;World Wide Web&#xff0c;万维网&#xff09;是一个全球性的信息空间&#xff0c;其中的文档和其他资源通过URL标识&#xff0c;并通过HTTP或其他协议访问。万维网是互联网的一个重要组成部分&#xff0c;但它并不是互联网的全部。互联…

【C++】类与对象的基础概念

目录&#xff1a; 一、inline 二、类与对象基础 &#xff08;一&#xff09;类的定义 &#xff08;二&#xff09;访问限定符 &#xff08;三&#xff09;类域 &#xff08;四&#xff09;实例化概念 正文 一、inline 在C语言的学习过程中&#xff0c;大家肯定了解过宏这个概…

matlab实现主成分分析方法图像压缩和传输重建

原创 风一样的航哥 航哥小站 2024年11月12日 15:23 江苏 为了研究图像的渐进式传输技术&#xff0c;前文提到过小波变换&#xff0c;但是发现小波变换非常适合传输缩略图&#xff0c;实现渐进式传输每次传输的数据量不一样&#xff0c;这是因为每次变换之后低频成分大约是上一…