C++11(上)

news2024/11/24 3:43:14

目录

1:列表初始化

2:std::initializer_list

3:变量类型推导

3.1:auto推导类型

3.2:decltype

3.3:nullptr

4:范围for

5:STL新增容器和容器新增接口

5.1:array

6:左值引用和右值引用

6.1:左值

6.2:右值

6.3:左值引用

6.4:右值引用

6.5:左值引用的场景和短板

 6.5.1:编译器关于构造的优化场景回顾

6.6:右值引用场景一:移动构造

6.7:右值引用场景二:移动赋值



1:列表初始化

C++11扩大了列表初始化的范围,旨在一切类型皆可用

在C语言中,列表初始化可以用于结构体的初始化。

struct date
{
	int month;
	int day;
};
int main()
{
	date d1 = { 8,4 };
	date d2[2] = { {1,1},{2,2} };
}

对于内置类型,我们可以通过花括号这样初始化

	int x1 = 1;
	int x2 = { 1 };
	int x3{ 1 };

但是这样有点鸡肋,感觉这个语法在c++11里面就是多余的成分。再看下面

可以在new对象,new变量的时候使用列表初始化。

	int* p1 = new int[4];//原本定义方式
	int* p2 = new int[4]{ 0 };//给4个0初始化
	int* p3 = new int[4]{ 0,1,2,3 };//用0123初始化

但是还是感觉没啥意义,再看下面,关于创建类对象的时候。

class date2
{
public:
	date2(int year,int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

	date2 dd1(2023, 4, 11);
    date2 dd2 = {2023,4,11};
	date2 dd3{2023,4,11 };

这里就是调用了构造函数进行初始化。

初始化列表的原理是什么呢?

2:std::initializer_list

C++11会自动识别花括号里面的内容为initializer_list的内容,举个例子:

	auto il = { 1,2,3,4 };
	cout << typeid(il).name() << endl;

 就像 这样。

其实initializer_list就是相当于保存常量的一个容器,经常用作构造函数的参数,也可以作为operator=的参数,可以用于花括号赋值。关于该容器,有这样几个接口:

用vector举个例子,vector可以这样初始化。

vector<int> v{ 1,2,3,4 };

从底层来说就是先用initializer_list保存花括号里面的常量,然后再遍历initializer_list,push_back到vector中。接下来模拟实现该原理。

namespace bit
{
	template<class T>
	class vector {
	public:
		typedef T* iterator;
		vector(initializer_list<T> l)
		{
			_start = new T[l.size()];
			_finish = _start + l.size();
			_endofstorage = _start + l.size();
			iterator vit = _start;
			typename initializer_list<T>::iterator lit = l.begin();
			while (lit != l.end())
			{
				*vit++ = *lit++;
			}
			//for (auto e : l)
			//   *vit++ = e;
		}
		vector<T>& operator=(initializer_list<T> l) {
			vector<T> tmp(l);
			std::swap(_start, tmp._start);
			std::swap(_finish, tmp._finish);
			std::swap(_endofstorage, tmp._endofstorage);
			return *this;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

同样的,list也能这样初始化。

3:变量类型推导

3.1:auto推导类型

	vector<int> v(10, 0);
	vector<int>::iterator it1 = v.begin();
	auto it2 = v.begin();
	map<string, string> m;
	map<string, string>::iterator it1 = m.begin();
	auto it2 = m.begin();

 auto可以实现自动类型判断,但也并非万能。

3.2:decltype

该关键字可以将变量声明为表达式指定的类型。如:

template<class T1,class T2>
void Func(T1 x, T2 y)
{
	decltype(x * y) ret = x * y;
	cout << ret << endl;
}

T1和T2可能是两个不同的非类型模板参数,比如int和double相乘,如果用T1或者T2去声明ret,就会有结果误差。因此decltype可以精准的推导出x*y的类型。

void Func(T1 x, T2 y)
{
	decltype(x * y) ret = x * y;
	auto ret2 = x * y;
	cout << ret <<" "<< ret2 << endl;
}
	Func(1, 2.2);
	Func(1, 3);

 这里auto一样可以推导类型,所以decltype在auto不能起作用的地方起作用,实际上意义不大,但是要认识这个关键字。

3.3:nullptr

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

c++中有人将NULL定义为0,所以在某些地方判断条件的时候用NULL会有bug,因此如果想判断某个指针为空,需要使用nullptr。

4:范围for

一个比较实用的语法糖,

有3个条件

  1. 迭代范围需确定
  2. 需要写出迭代器的begin和end接口,左闭右开
  3. 迭代器需要支持++和==

void TestFor(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}

这个就不能迭代,因为array的范围不确定。

5:STL新增容器和容器新增接口

 最下面2个应该不陌生,是比较方便查找的哈希表。

5.1:array

array是一个静态数组,相比较于c语言中的数组,多了一个越界写可以检查出来的机制。

	int arr[10];
	arr[10];
	arr[20] = 1;

c语言中的数组越界读没问题,而越界写则是随机检查。

	int arr[10];
	arr[10];
	arr[20] = 1;
	arr[10] = 1;

 当对10下标的元素进行写的时候,报错,对20下标不报错。因此C语言的数组只能随机对写进行检查。对读不检查。

而array既能对读检查,也能对写检查。

	array<int,10> a;
	a[10];

 读报错。

	array<int,10> a;
	a[10] = 1;

 写报错。

但是其实这样不如使用我们的vector。

6:左值引用和右值引用

6.1:左值

左值:是一个数据的表达式(如变量名或者指针解引用),可以在该语句之后还能获取到他的地址,能对他进行取地址和赋值操作,左值既可以在赋值符号左边,也可以在右边。

	// 以下的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;

6.2:右值

右值:也是一个数据表达式,通常比如:字面常量,传值返回的函数的返回值,表达式返回值。

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;

 fmin的函数返回值不能是左值引用,否则该返回值就不是右值。

需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可
以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地
址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感觉很神奇,
这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要。
 double x = 1.1, y = 2.2;
 int&& rr1 = 10;
 const double&& rr2 = x + y;
 rr1 = 20;
 rr2 = 5.5;  // 报错

6.3:左值引用

  1. 左值引用只能引用左值。
  2. const左值引用既可以引用左值,也可以引用右值。
int a = 10;
const int& x1 = a;
const int& x2 = 10;

6.4:右值引用

  1. 右值引用只能引用右值。
  2. 右值可以引用左值move之后的值。
	int a = 10;
	int&& x1 = 10;
	int&& x2 = std::move(a);

6.5:左值引用的场景和短板

用于函数引用传参,和传引用返回,可以减少拷贝。

namespace wjw
{
	const string& to_string(const string& x)
	{
		return x;
	}
}

	wjw::to_string("abcd");

x是一个静态变量,可以直接返回x,且传引用返回没问题,减少值拷贝。但是如果碰到下面的情况呢?

	const string& to_string(const string& x)
	{
		string str;
		return str;
	}

这样写就有很大的问题,str是一个局部变量,出了函数作用域就会销毁,这里用左值引用返回就是返回一个已经销毁的变量,很明显不行。

所以需要使用传值返回,但是如果是传值返回,我们在函数栈帧学过,返回临时变量的时候,会创建一个临时变量,再把str拷贝构造给临时变量,这样就多了一次拷贝,这个时候就需要使用右值引用返回。

这里分为2步:

  •  创建临时对象,str拷贝构造给临时对象,临时对象拷贝构造给ret2,因为这个时候ret2是正在创建的,不是已经创建好的(所以ret2=这一步是拷贝构造,不是赋值重载)这里是一个拷贝构造+拷贝构造,编译器会自动优化成一次拷贝构造,就是将ret直接拷贝构造给ret2。

 6.5.1:编译器关于构造的优化场景回顾

void f1(A aa)
{}
A f2()
{
 A aa;
 return aa;
}
int main()
{
 // 传值传参
 A aa1;
 f1(aa1);
 cout << endl;
 // 传值返回
 f2();
 cout << endl;
 // 隐式类型,连续构造+拷贝构造->优化为直接构造
 f1(1);
 // 一个表达式中,连续构造+拷贝构造->优化为一个构造
 f1(A(2));
 cout << endl;
 // 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
 A aa2 = f2();
 cout << endl;
 // 一个表达式中,连续拷贝构造+赋值重载->无法优化
 aa1 = f2();
 cout << endl;

6.6:右值引用场景一:移动构造

 前面说过,连续的拷贝构造编译器会优化成一个拷贝构造,因为to_string是传值返回,返回值是一个右值,用这个右值去拷贝构造ret2,因为ret2的拷贝构造函数的参数是一个const左值引用,所以没有编译错误,这里就是调用了一个深拷贝。

拷贝的代价太大了如何解决?

str反正是一个要销毁的变量了,我们何妨不直接把他的资源偷窃给ret2呢?这里需要用到右值引用.。

string(string&& s)
   :_str(nullptr)
   ,_size(0)
   ,_capacity(0)
{
cout<<"string(string&& s)"<<"移动语义"<<endl;
swap(s);
}

c++中的右值有2种

 顾名思义,str是一个即将销毁的变量,我们直接对他进行引用,把他的资源偷窃给_str,就无需使用拷贝构造了。

6.7:右值引用场景二:移动赋值

 ret1是已经创建好的变量,之前说过拷贝构造+赋值重载不能进行优化,所以这里会发生2个步骤,先创建一个临时对象,编译器在这里会很聪明的把str的返回值识别成一个右值,这样的话临时对象就不会调用拷贝构造str,而是直接移动构造str,然后再把临时对象作为to_string的返回值赋值给ret1。所以结果会是一个移动构造+一个移动赋值

这里总结:

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

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

相关文章

python常用库之time库

目录 一、前言time库中的常用函数 二、time()函数三、localtime()和gmtime()函数四、strftime() 、asctime()、mktime()函数&#xff08;一&#xff09;strftime()函数&#xff08;二&#xff09;asctime()函数&#xff08;三&#xff09;mktime()函数 五、ctime()函数六、stri…

【2023最新】超详细图文保姆级教程:App开发新手入门(5)

上文回顾&#xff0c;我们已经完成了一个应用的真机调试&#xff0c;本章我们来了解一下如何引入YonBuilder移动开发的&#xff08;原生&#xff09;移动插件, 并利用移动插件完成一个简单的视频播放器。 8. 「移动插件」的使用 8.1 什么是 「移动插件」&#xff1f; 用通俗…

TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:1~5

原文&#xff1a;Mobile Deep Learning with TensorFlow Lite, ML Kit and Flutter 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的…

【高危】Apache Spark 权限提升漏洞(CVE-2023-22946)

漏洞描述 Apache Spark 是一款支持非循环数据流和内存计算的大规模数据处理引擎。 使用 spark-submit 脚本在集群中启动任务可以通过指定proxy-user参数限制运行用户。在 Apache Spark 受影响版本中&#xff0c;攻击者通过指定自定义的classpath&#xff0c;则可以覆盖该配置…

2023最新面试题-Java-3

IO流 1. java 中 IO 流分为几种? 按照流的流向分&#xff0c;可以分为输入流和输出流&#xff1b;按照操作单元划分&#xff0c;可以划分为字节流和字符流&#xff1b;按照流 的角色划分为节点流和处理流。 Java Io 流共涉及 40 多个类&#xff0c;这些类看上去很杂乱&…

ChatGPT 70+款可以免费使用的AI工具,建议收藏

ChatGPT风靡全球&#xff0c;人人可用&#xff01; 小红书上有关ChatGPT的笔记已有10w篇&#xff0c;相关话题浏览量也达到了1.12亿次。其中讨论最为热烈的&#xff0c;要数“ChatGPT使用教程”。&#xff08;当然&#xff0c;类似的话题还包括&#xff0c;教你如何使用Midjour…

Navicat图表查看器 Crack

Navicat图表查看器 Crack Navicat图表查看器是一个查看图表工作区文件的简单工具。您可以浏览Navicat的图表工具和Navicat图表创建者创建的区域。 Navicat图表查看器&#xff0c;将图表中的数据显示为强大的可视化效果&#xff0c;允许您使用图形和图表查看数据。 将您的信息转…

Internet Download Manager(IDM)v6.41.11 免激活不弹窗版

Internet Download Manager&#xff08;IDM&#xff09;v6.41.11 免激活不弹窗版可提升你的下载速度多达5倍&#xff0c;安排下载时程&#xff0c;或续传一半的软件。Internet Download Manager的续传功能可以恢复因为断线、网络问题、计算机宕机甚至无预警的停电导致下传到一半…

大数据开发必备面试题Flume篇合集

大数据开发必备面试题Flume篇合集 1 、详细介绍Flume有哪些组件&#xff1f;2、你是如何实现Flume数据传输的监控的&#xff1f;3、Flume参数怎么调优&#xff1f;4、简述下Flume的事务机制。5、 Flume采集数据会丢失吗?6、简述下Flume使用场景。7、简述下 Flume丢包问题。8、…

【C语言】文件操作

目录 1.为什么使用文件 2.什么是文件 2.1 程序文件 2.2 数据文件 2.3文件名 3.文件的打开和关闭 3.1文件指针 3.2文件的打开和关闭 4.程序的顺序读写 4.2对比一组函数 5.文件的随机读写 5.1 fseek 5.2 ftell 5.3 rewind 6.文本文件和二进制文件 7.文件读取结束的…

2023年银行理财子公司研究报告

第一章 行业发展概况 1.1 行业概况 所谓“银行理财子公司”&#xff0c;其实就是由商业银行作为控股股东发起设立的&#xff0c;并经国务院银行业监督管理机构批准&#xff1b;主要从事理财业务&#xff1b;独立于母行&#xff0c;具有独立法人地位的非银行金融机构。像工商银…

Redission分布式锁

实现过程&#xff1a; 只要线程一加锁成功&#xff0c;就会启动一个 watch dog 看门狗&#xff0c;它一个后台线程&#xff0c; 会每隔 10 秒检查一下&#xff0c;如果线程 1 还持有锁&#xff0c;那么就会不断延长锁 key 生存时间。因此&#xff0c;Redisson 解决了锁过期释放…

Commitizen规范git提交代码

首先全局安装Commitizen&#xff0c;运行&#xff1a; npm install -g commitizen 然后在项目中开启终端&#xff0c;安装cz-customizable npm i cz-customizable --save-dev 然后在package.json中配置如下代码&#xff1a; "config": {"commitizen":…

虹科干货| 虹科Redis企业版数据库:告别游戏卡顿,让快乐加速!

“卡顿一分钟&#xff0c;玩家两行泪” 游戏已成为年轻人最主要的消遣娱乐方式之一&#xff0c;游戏卡顿给玩家带来糟糕游戏体验背后的原因是什么&#xff1f;数据存储与查询速度不够快&#xff01; 游戏开发领域&#xff0c;不仅拥有海量的数据&#xff0c;甚至还要做到实时…

Flowable6.x导出/查看/跟踪流程图

Flowable6.x导出/查看/跟踪流程图 项目源码仓库 Flowable诞生于Activiti&#xff0c;是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义&#xff0c;可以十分灵活地加入你的应用/服务/构架。 本文介绍4种绘制流程图的方式&#xff0c;前…

TryHackMe-Set(Windows渗透测试 | WinDefender免杀)

Set 您再次发现自己在Windcorp公司的内部网络上。上次你去那里的味道真好&#xff0c;你回来了 了解更多。 但是&#xff0c;这次他们设法保护了域控制器&#xff0c;因此您需要找到另一台服务器&#xff0c;并在第一次扫描时发现“Set”。 Set被用作开发人员的平台&#xf…

集团企业大数据治理解决方案word

第一章 集团企业大数据治理阶段目标 通过数据平台和BI应用建设&#xff0c;集团企业大数据将搭建统一的大数据共享和分析平台&#xff0c;对各类业务进行前瞻性预测及分析&#xff0c;为集团企业各层次用户提供统一的决策分析支持&#xff0c;提升数据共享与流转能力。 一.1、…

Redis持久化的几种方式

Redis 持久化也是 Redis 和 Memcached 的主要区别之一&#xff0c;因为 Memcached 是不具备持久化功能的。 1.持久化的几种方式 Redis 持久化拥有以下三种方式&#xff1a; 快照方式&#xff08;RDB, Redis DataBase&#xff09;将某一个时刻的内存数据&#xff0c;以二进制的…

SpringAOP入门基础银行转账实例(进阶版)------------事务处理

SpringAOP入门基础银行转账实例**&#xff08;进阶版&#xff09;**------------事务处理 由上一节讲述的通过Connection和QueryRunner对事务进行的处理(详情可以去我之前写的博客文章&#xff1a;https://blog.csdn.net/m0_56245143/article/details/130069160?spm1001.2014…

派盘为您的个人数据安家

现如今,我们的生活中有着各种各样的数据。在工作中会有各种文件、邮件;在生活中则有照片和视频等。数据的来源多,时间点不一致且混乱。 数据是否能安全、稳定、长久的存储以及便捷高效的使用对我们来说相当重要。你是否经常出差需要带上电脑或者移动硬盘,想存网盘又怕丢失或…