C++11,{}初始化,initializer_list,decltype,右值引用,类和对象的补充

c++98是C++标准委员会成立第一年的C++标准,C++的第一次更新是C++03,但由于C++03基本上是对C++98缺陷的修正,所以一般把C++98与C++03合并起来,叫做C++98/03;

后来原本C++委员会更新的速度预计是5年更新一次,但由于C++标准委员会的进度,吃吃未更新,直到2011年C++11的出现使得C++的功能出现了大的提升,所以我们需要进行学习,提高我们的编码效率;

{}初始化

在c++11中,我们的初始化有更多的方式,{}这个符号也可以进行数据的初始化,这是在C++98中没有的;

//										测试C++11{}符号初始化
int main()
{
	int a = 10;
	int b = { 10 };
	int c{ 10 };
	vector<int> v{ 1,2,3,4,5 };
	int* pa = { &a };
	int arr[]{ 1,2,3,4 };

	return 0;
}

这些都是可以使用{}花括号来进行初始化的;

我们自己实现的类使用{}也会自动调用构造函数来进行初始化:

class A {
public:
	A(string str)//单参数构造参数可以自动进行类型转化
		:_a(str)
	{
		cout << "A() 构造函数" << endl;
	}
private:
	string _a;
};
int main()
{
	A a{ "123" };
	return 0;
}

 看自动调用了构造函数:

我们使用的时候用起来非常方便但是出现这样的使用情况的时候是不是会有点奇怪,这是怎么构造的:

list<int> l{1,2,3,4,5,6,7}; 

奇怪了,为什么这样也可以进行构造,如果是调用普通的构造函数,我们传递这么多参数,难道构造函数有这么多参数吗,而且参数的个数还是不确定的;

所以为了解决这个疑惑引入一个新的概念:

initializer_list 模板类

这是一个C++的模板类,这个类可以用来接收{}中的数据,构造出一个装载了{}中类型数据的list;所以说我们在使用{}进行初始化容器时,如果容器无法直接调用构造函数初始化,就会尝试先将数据转换为initializer_list然后再将initializer_list中的数据交给容器的构造函数来进行构造;

我们看容器的构造函数就可以看到容器是适配了一个参数是initializer_list构造函数的

基本上在C++11中所有的容器都是实现了这样的构造来适配{}进行初始化的;

map<int,string> m{ { 123 , "abc" } , { 456 , "def" } };

它们都是适配了initializer_list的;

我们自己实现的list也需要再多实现一个构造这样我们也能支持{}的初始化了:

		list(initializer_list<int> lt)
		{
			list_emptyinit();
			for (auto& e: lt)
			{
				push_back(e);
			}
		}
        
        list<int> l{ 1,2,3,4,5,6 };

decltype 

 这个关键字可以用来获取我们的表达式结果类型并返回此类型:

自动推导出了类型vector中的数据类型为double;

C++11增加的容器:

unordered_map   ,unordered_set    ,array,   forward_list; (记之以忘)

右值引用

左右值简介

在c++中有右值与左值之分,一开始就是是用在赋值符号的作用来作为区分的;但是现在对于左右值不能这么区分;现在应该如此区分:可以取地址的就是左值,不可取地址的为右值;

右值一般为将亡值,就是即将被释放的数据,(但有时候我们用户强制在转换的右值可能不一定是将亡值)

左右值可以相互转换(但有条件)

//												右值引用
int main()
{
	string a = "hello";
	string b = " world!";
	//左值引用
	string& refa = a;
	const string& refab = a + b;//可以使用const来接收右值(因为临时对象具有常性)

	//右值引用
	string&& rrefb = move(b);//可以使用move函数将左值转换为右值来接收接收
	string&& rrefab = a + b;

	list<int> l{ 1,2,3,4,5,6 };
	list<int> l1 = move(l);//拷贝构造时右值的数据会被转移走
 
	return 0;
}

这里的l不但不是将亡值而且还是左值,但是因为move将它转换为了右值,使得他的数据被转移走了;

所以右值的转换要慎用; 

左值:就是我们常见的普通数据(不过多讲解)

右值:

1.  将亡值    

2.(我们不需要再使用的数据)可以将它转换为右值提供给某个接口或者资源,进行资源转移;

移动构造函数:

在容器中,为了减少拷贝带来的消耗,如果传递给容器的值是右值则会调用移动构造来进行拷贝,依此来提高效率

//								我发现编译器对于拷贝构造进行了很多的优化,有时候并不会调用拷贝构造
struct A
{
	string _str;
	A(const string& str)
		:_str(str)
	{
		cout << "构造函数A(const string& str)" << endl;
	}
	A()
		:_str("")
	{
		cout << "构造函数A()" << endl;
	}
	A(const A& a)
		:_str(a._str)
	{
		cout << "深拷贝A(const A& a)" << endl;
	}
	A(A&& a)
	{
		swap(_str, a._str);
		cout << "移动构造A(A&& a)" << endl;
		(void)a;
	}
	~A()
	{}
};

int main()
{
	string s("hello");
	A a(s);//普通构造函数
	A c(a);//深拷贝
//A b(A());//这样会被编译器翻译为一个函数的声明,声明了一个叫b的函数,返回值类型为A,参数为返回值为A的无参函数
//A b(A{});//本来要调用移动构造的但是,由于编译器优化,直接进行了一次构造
	A b(move(a));//移动构造
 

	return 0;
}

这里由于a是右值,所以a的资源会被转移给,b使得b不用进行深拷贝(再开辟一片空间出来复制一份a的数据);

这样可以大大的提高拷贝的效率;这就是右值的运用;

运用场景

编译器未优化前:

左值拷贝时的场景:

看这就是深拷贝的场景,因为这样的情况,为了解决这样低效的拷贝,右值引用应运而生;

右值拷贝的场景:

 如果我们使用右值引用来接收这个返回值v

编译器优化后:

左值引用:

右值引用:

 移动语义

为什么说这里的&和&&标志可以省略呢,因为,前面我们说了拷贝构造和移动构造的出现使得我们的拷贝有了两种方式,1.是进行深拷贝,2.是进行移动构造;而这两种构造的方式是通过不同的参数来调用的,编译器会匹配最适合的函数让参数进入;而我们将&和&&标志省略后,编译器判断出这个返回值是局部变量他是右值,匹配最合适的移动构造来进行拷贝,这就是移动语义;当然如果没有移动构造,那么就只能匹配普通的构造函数来来构造了,移动语义也没办法去调用不存在的移动构造函数;

下面是我将移动构造加入我自己实现的strin类中,从而实现移动构造的场景的代码:

模拟实现stl容器/模拟实现string类 · future/my_road - 码云 - 开源中国 (gitee.com)

 完美转发

有的时候,我们的右值因为多次的传递它不再是右值了,这个时候我们的移动语义就无法被触发,从而无法调用移动构造;

//												完美转发

void func(const string& a)
{
	cout << "左值" << endl;
}

void func(string && a)
{
	cout<<"右值" << endl;
}

template<class T>
void test1(T&&a)
{
	//func(a);//a引用接收右值之后,他自己便成为了左值

	func(forward<T>(a));//完美转发,保持a右值的状态
}

void test()
{
	string a;
	test1(a);
	test1(move(a));
}

int main()
{
	test();
	return 0;
}

 在模板中肯定会有这种多层传递的出现,一个右值传递给了右值引用,此时的右值引用是左值,因为它将右值存储器起来了,这个右值引用可以取地址了;

所以为了避免多次传递时右值被转换为了左值,我们需要使用forward<T>()这个函数来保持原有类型状态,保持它的右值的状态;我们需要在每一层都使用forward<T>来保证右值的传递,直到最后一层的右值传递给移动构造即成功完成传递;

模板中的&&

在模板中&&可以即代表左值又代表右值叫做(万能引用),编译器会根据传递给接口的参数类型来进行推到的;

push_back(T&&data)

data为左值时->T&

data为右值时->T&&

对于匿名对象的说明 

//									测试临时对象返回
struct A
{
	int _a ;
	int _b ;
	A(const A& a)
	{
		_a = a._a;
		_b = a._b;
		//noting
	}
	//A &()(int a,int b)
	A(int a,int b)
		:_a(a)
		,_b(b)
	{	
	}
};
A getA()
{
	A a(10, 20);
	return a;
}
int main()
{
	const A& b = getA();
	string("11111111111");
}

我们可以使用引用来接收匿名对象/临时对象时,匿名/临时对象会延长生命周期至b的生命周期,直到b死亡这份空间中的资源才会被释放;

如果我们修改返回值为引用:

A &()(int a,int b)

{

        return A;

那么这个时候返回的值指向的时被释放的内存的局部变量A;如果我们在继续增加栈帧就会覆盖掉原来的数据从而会影响b引用的数据:

 类和对象的补充

在C++98中有6个默认函数,到了c++11后增加移动构造和移动赋值两个默认成员函数;

默认生成的移动构造和移动赋值:

当我们没有实现析构,拷贝构造,赋值构造三个中的任意一个默认函数时,编译器会自动生成一个移动构造函数,这个移动构造函数会自动的调用成员的移动构造,如果成员没有移动构造则调用拷贝构造函数(移动赋值构造是同样的原理);

我们可以看下面的代码:

//					证明移动构造的默认生成
class B {
public:
	B()
	{}
	B(const char* str)
		:_str(str)
	{
	}
	B(const B& b)
		:_str(b._str)
	{
		cout << "(const B& b)深拷贝" << endl;
	}
	B(B&& b)
	{
		_str.swap(b._str);
		cout << "B(B&& b)移动构造" << endl;
	}
private:
	string _str;
};

class A {
public:
	A(const B& data)
		:_data(data)
	{
	}

	//~A()//只要写了一个除构造外的默认函数就会导致默认移动构造的删除
	//{}

private:
	B _data;
};

int main()
{
	B b("hello");
	A a(b);
	A a1(a);//深拷贝
	A a2(move(a));//移动构造我们并没有实现,但是还是调用了B的移动构造,说明默认生成了
	A a3 = move(a2);//移动赋值也是默认生成了的(b没有实现赋值调用了)

	return 0;
}

现象: 

 

 当写了析构函数的时候:

 

内置类型的初始化(缺省参数)

在C++11中补齐了前面C++98中,只有内置类型会自动调用自己的构造函数缺陷,在下面函数定义的位置可以设置缺省参数来初始化成员变量;

default关键字 

这个关键字可以生成默认的拷贝构造,赋值构造,移动构造,移动赋值函数(暂时我只知道这四个函数默认生成的情况,其他如果还有我们再慢慢补充)

生成场景,就拿上面的默认移动构造的生成来举例,假设我们自己实现了一个析构函数(一般自己实现说明是深拷贝,但我们这里只是为了产生现象所以写一个,但是内部不写任何代码)

class B {
public:
	B()
	{}
	B(const char* str)
		:_str(str)
	{
	}
	B(const B& b)
		:_str(b._str)
	{
		cout << "(const B& b)深拷贝" << endl;
	}
	B(B&& b)
	{
		_str.swap(b._str); 
		cout << "B(B&& b)移动构造" << endl;
	}
private:
	string _str = "hello";
};

class A {
public:
	A(const B& data)
		:_data(data)
	{
	}

	~A()//只要写了一个除构造外的默认函数就会导致默认移动构造的删除
	{}

	A(const A& a) = default;//让编译器默认生成
	A(A&& a) = default;//让编译器默认生成

private:
	B _data;
};

int main()
{
	B b("hello");
	A a(b);
	A a1(a);//深拷贝
	A a2(move(a));//移动构造我们并没有实现,但是还是调用了B的移动构造,说明默认生成了
	A a3 = move(a2);//移动赋值也是默认生成了的(b没有实现赋值调用了)

	return 0;
}

看编译器删除了默认构造后又生成了,这就是default的作用;

delete关键字 

这是用来禁止生成默认函数的,C++98中是将那个不许生成的默认函数作为private成员这样一旦调用此函数就会报错,但是在C++11中,直接使用=delete,即可完成禁止生成的操作;

class B {
public:
	B()
	{}
	B(const char* str)
		:_str(str)
	{
	}
	B(const B& b)
		:_str(b._str)
	{
		cout << "(const B& b)深拷贝" << endl;
	}
	B(B&& b)
	{
		_str.swap(b._str); 
		cout << "B(B&& b)移动构造" << endl;
	}
private:
	string _str = "hello";
};

class A {
public:
	A(const B& data)
		:_data(data)
	{
	}

	//~A()//只要写了一个除构造外的默认函数就会导致默认移动构造的删除
	//{}

	//A(const A& a) = default;//让编译器默认生成
	//A(A&& a) = default;//让编译器默认生成

	A(const A& a) = delete;//阻止编译器默认生成
	A(A&& a) = delete;//阻止编译器默认生成
private:
	B _data;
};

int main()
{
	B b("hello");
	A a(b);
	A a1(a);//深拷贝
	A a2(move(a));//移动构造我们并没有实现,但是还是调用了B的移动构造,说明默认生成了
	A a3 = move(a2);//移动赋值也是默认生成了的(b没有实现赋值调用了)

	return 0;
}

委托构造 

在C++11中,我们还可以在一个构造函数中去调用另一个构造函数;这之前只有在继承的时候,子类需要显示的调用父类的带参构造去初始化父类的变量;而C++11可以通过在初始化列表中显示的委托构造调用自己的构造函数;

class A {
public:
	A()
	{
		cout << "A()" << endl;
	}

	A(int a)
		:A()//委托构造
	{
		cout << "A(int a)"<<endl;
		_a = a;
	}
private:
	int _a;
};
int main()
{
	A a(10);
	return 0;
}

 现象:

 

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

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

相关文章

SOLIDWORKS Electrical电气智能零部件的运用

电气2D向电气3D转型&#xff0c;3D模型无疑是重中之重&#xff0c;精准、正确的3D模型有利于电线长度、空间大小、耗材的计算。而线槽、导轨因为要根据实际情况裁剪&#xff0c;所以即使同一规格的线槽、导轨&#xff0c;在装配时也得根据实际情况&#xff0c;修改长度&#xf…

AAA、RADIUS、TACACS、Diameter协议介绍

准备软考高级时碰到的一个概念&#xff0c;于是搜集网络资源整理得出此文。 概述 AAA是Authentication、Authorization、Accounting的缩写简称&#xff0c;即认证、授权、记帐。Cisco开发的一个提供网络安全的系统。AAA协议决定哪些用户能够访问服务&#xff0c;以及用户能够…

linux系统-PXE高效批量网络装机

目录 一、PXE概述 PXE批量部署的优点 搭建PXE网络体系的前提条件 二、搭建PXE远程安装服务器 1.修改网络配置 2 .老样子关防火墙&#xff01;&#xff01;&#xff01;&#xff01; 3.确保挂载状态 和yum库 4. 安装TFTP服务 5.修改TFTP服务的配置文件 6.启动服务 7…

概念解析 | 基础模型:AI时代的“形而上学“

注1:本文系"概念解析"系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:基础模型(Foundation Models) 概念解析 | 基础模型:AI时代的"形而上学" What Are Foundation Models? | NVIDIA Blogs 第一部分:通俗解释 基础模型(…

echars设置渐变颜色的方法

在我们日常的开发中&#xff0c;难免会遇到有需求&#xff0c;需要使用echars设置渐变的图表&#xff0c;如果我们需要设置给图表设置渐变颜色的话&#xff0c;我们只需要在 series 配置项中 添加相应的属性配置项即可。 方式一&#xff1a;colorStops type&#xff1a;‘lin…

this关键字

this 文章目录 this引出Thisthis的作用this.属性内存分析 this.方法名this(&#xff09;构造方法 概念&#xff1a;this 关键字是 Java常用的关键字&#xff0c;可用于任何实例方法内指向当前对象&#xff0c;也可指向对其调用当前方法的对象&#xff0c;可以将this理解为一个指…

被问了n遍的小程序地理位置权限开通方法

小程序地理位置接口有什么功能&#xff1f; 在平时我们在开发小程序时&#xff0c;难免会需要用到用户的地理位置信息的功能&#xff0c;小程序开发者开放平台新规要求如果没有申请开通微信小程序地理位置接口( getLocation )&#xff0c;但是在代码中却使用到了相关接口&#…

【3dmax笔记】023:阵列工具(移动+一维+二维+三维)

文章目录 一、阵列工具二、案例演示 一、阵列工具 【阵列】命令将显示【阵列】对话框&#xff0c;使用该对话框可以基于当前选择创建对象阵列。 菜单栏&#xff1a;【工具】菜单 > 【阵列】 二、案例演示 首先&#xff0c;画一个物体&#xff0c;如茶壶&#xff0c;如下图…

Ps 中 曲线和色阶的区别在哪里?

【官方解释】 在Photoshop中&#xff0c;曲线&#xff08;Curves&#xff09;和色阶&#xff08;Levels&#xff09;是两种调整图像色调和对比度的工具&#xff0c;它们有一些相似之处&#xff0c;但也有一些重要的区别。 调整方式: 曲线&#xff08;Curves&#xff09;&…

04-19 周四 GitHub CI 方案设计

04-19 周四 GitHub CI 方案设计 时间版本修改人描述2024年4月19日14:44:23V0.1宋全恒新建文档2024年4月19日17:22:57V1.0宋全恒完成部署拓扑结构的绘制和文档撰写 简介 需求 由于团队最近把代码托管在GitHub上&#xff0c;为解决推理、应用的自动化CI的需要&#xff0c;调研了…

【C语言刷题系列】移除元素

目录 一、问题描述 二、解题思路 三、源代码 个人主页&#xff1a; 倔强的石头的博客 系列专栏 &#xff1a;C语言指南 C语言刷题系列 一、问题描述 二、解题思路 在C语言中&#xff0c;原地移除数组中所有等于特定值的元素并返回新长度的问题可以通过双指针法…

虚拟化之---virtio通信

一、理解virtio的背景 我们知道虚拟化hypervisor大的类型分为两种&#xff0c;全虚拟化和半虚拟化。 在全虚拟化的解决方案中&#xff0c;guest VM 要使用底层 host 资源&#xff0c;需要 Hypervisor 来截获所有的请求指令&#xff0c;然后模拟出这些指令的行为&#xff0c;这样…

python-dict序列化的数据为啥前后不一致

前情提要及背景:流式数据的二次处理终结篇-CSDN博客 假如直接将dict进行str,那么编码数据都是一致的,但是在postman上就表现不那么好看,如下: 而之前的显示如下: 其中的差别就是单引号与双引号的差别了。 采用如下方案无疑是最笨的方法了: 在Python中,如果你想将处理…

各城市-人口就业和工资数据(1978-2022年)

这份数据收集了1978年至2022年间300多个地级市的人口、就业和工资等数据。涵盖的指标包括从业人员数量、平均工资水平、人口密度等&#xff0c;通过这些数据可以深入了解中国各地城市的人口结构、就业状况以及工资水平的变化趋势。这些数据对于研究城市发展、劳动力市场以及区域…

微积分 --- 偏导数,方向导数与梯度(二)

方向导数 上图为一温度图&#xff0c;所反映的是加利福利亚洲和内华达州在十月的一天下午三点的温度。其中&#xff0c;图中的每一点都是温度T关于x,y的函数&#xff0c;即T(x,y)。对于图中的Reno市而言&#xff0c;沿着x方向的偏导反映的是温度沿着x方向&#xff0c;即沿着东方…

【搜索技能】外链

文章目录 前言一、外链是什么&#xff1f;二、如何进行外链调查&#xff1f;总结 前言 今儿因为在搜索一个很感兴趣的软件&#xff0c;但是软件信息所在的网址非常有限。因此产生了一个念头&#xff1a;我能不能找到所有的包含了或者是引用了这个网站的网站呢? 调查之下&…

五道链表习题,只过思路

建议先过一遍&#xff1a;保研机试前的最后七道链表题-CSDN博客 第一题 82. 删除排序链表中的重复元素 II - 力扣&#xff08;LeetCode&#xff09; 是不是似曾相识的感觉&#xff0c;好像数组顺序去重&#xff0c;请看&#xff1a;保研机试前的最后七道数组题-CSDN博客 第二…

幻兽帕鲁游戏主机多少钱?幻兽帕鲁游戏服务器一个月仅需32元

随着游戏产业的蓬勃发展&#xff0c;腾讯云紧跟潮流&#xff0c;推出了针对热门游戏《幻兽帕鲁Palworld》的专属游戏服务器。对于广大游戏爱好者来说&#xff0c;这无疑是一个激动人心的消息。那么&#xff0c;腾讯云幻兽帕鲁游戏主机到底多少钱呢&#xff1f;让我们一起来揭晓…

编程基础学什么课程内容

编程基础学习的课程内容有&#xff1a;程序设计基础、算法与数据结构、计算机科学原理、面向对象编程、网页开发基础等课程内容&#xff0c;以下是上大学网 (www.sdaxue.com)整理的具体课程或技能领域内容&#xff0c;供大家参考&#xff01; 程序设计基础&#xff08;或计算机…

重学java 29.经典接口

光阴似箭&#xff0c;我好像跟不上 —— 24.5.6 一、java.lang.Comparable 我们知道基本数据类型的数据(除boolean类型外)需要比较大小的话&#xff0c;直接使用比较运算符即可&#xff0c;但是引用数据类型是不能直接使用比较运算符来比较大小的。那么&#xff0c;如何解决这个…