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

news2024/9/23 5:19:20

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;
}

 现象:

 

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

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

相关文章

2×24.5W、内置 DSP、低失真、高信噪比、I2S 输入 D 类音频功率放大器,完美替换TPA5805,晶豪,致盛,

ANT3825 是一款高集成度、高效率的双通道数字 输入功放。供电电压范围在 5V&#xff5e;18V&#xff0c;数字接口 电源支持 3.3V 或 1.8V。双通道 BTL 模式下输出 功率可以到 224.5W(4Ω&#xff0c;16V&#xff0c;THDN1%)&#xff0c; 单通道 PBTL 模式下可以输出 37W&#x…

软件测试产品交付包括哪些内容?

软件测试产品交付通常会包括以下内容: 1. 测试计划:详细的测试方案、测试范围、测试资源与时间安排等内容。 2. 测试用例:包括功能测试用例、性能测试用例、安全测试用例等各类测试用例。 3. 测试环境:包括硬件环境、软件环境、网络环境、数据环境等测试所需要的各种环境。 4. …

Chrome提示(已屏蔽:mixed-content)

这是提示信息&#xff0c;它的含义就是你在https的站点里面去请求了非https的资源&#xff0c;(我这里就是这个情况&#xff0c;web页是https的&#xff0c;但是接口是http的)&#xff0c;所以就会出现这个问题&#xff0c;解决办法也很简单&#xff0c;给它套上证书就行了

【考研数学】武忠祥「基础篇」如何衔接进入强化?

如果基础篇已经做完&#xff0c;并且讲义上的例题也都做完了&#xff0c; 那下一步就是该做题了 这个时候&#xff0c;不能盲目做题&#xff0c;做什么题很重要&#xff01;我当初考研之前&#xff0c;基础也很差&#xff0c;所以考研的时候选了错误的题集&#xff0c;做起来就…

有什么方便的教学口语软件?6个软件教你快速练习口语

有什么方便的教学口语软件&#xff1f;6个软件教你快速练习口语 以下是六个方便实用的教学口语软件&#xff0c;它们可以帮助您快速练习口语&#xff1a; AI外语陪练: 这是一款知名的语言学习软件&#xff0c;提供多种语言的口语练习课程。它采用沉浸式的学习方法&#xff0…

020、Python+fastapi,第一个Python项目走向第20步:ubuntu 24.04 docker 安装mysql8集群+redis集群(一)

系列文章 pythonvue3fastapiai 学习_浪淘沙jkp的博客-CSDN博客https://blog.csdn.net/jiangkp/category_12623996.html 前言 docker安装起来比较方便&#xff0c;不影响系统整体&#xff0c;和前面虚拟环境有异曲同工之妙&#xff0c;今天把老笔记本T400拿出来装了个ubuntu24…

HCIP的学习(12)

OSPF优化 ​ OSPF的优化主要目的是为了减少LSA的更新量。 路由汇总-----可以减少骨干区域的LSA数量特殊区域-----可以减少非骨干区域的LSA数量 OSPF路由汇总 域间路由汇总-----在ABR设备上进行操作 [GS-R2-ospf-1-area-0.0.0.1]abr-summary 192.168.0.0 255.255.224.0 [GS-…

分享8000网剧资源

兄弟们&#xff0c;前段时间搞短剧&#xff0c;收集了8500多部网剧资源。搞了整整两个月就赚3块两毛八&#xff0c;电费都不够。还不如进厂打螺丝。果断放弃这项目。 资源在手里面也没啥用。分享出来&#xff0c;大家看着玩。 有其他好的网络项目也可以分享分享。也可也一起…

优化|大语言模型中的优化问题(LoRA相关算法)

一、LoRA 在大语言模型中&#xff0c;参数矩阵 W ∈ R d d W\in \mathbb{R}^{d \times d} W∈Rdd的维度往往可以达到百亿甚至千亿&#xff0c;如果从头开始训练将会特别的消耗时间和资源。因此往往大家都会预先训练好一组初始参数 W 0 ∈ R d d W_0\in \mathbb{R}^{d \times…

git-新增业务代码分支

需求 使用git作为项目管理工具管理项目&#xff0c;我需要有两个分支&#xff0c;一个分支是日常的主分支&#xff0c;会频繁的推送和修改代码并推送另外一个是新的业务代码分支&#xff0c;是一个长期开发的功能&#xff0c;同时这个业务分支需要频繁的拉取主分支的代码&#…

【代码实践】starRocks 窗口函数(udf)实践

背景说明 实现天粒度的同比计算重点说明 要求数据是连续的因为天粒度的同比&#xff0c;需要365天&#xff0c;但为了方便测试&#xff0c;当前的判断逻辑是计算5天的前&#xff0c;而不是365天前的 参考文档 https://docs.starrocks.io/zh/docs/sql-reference/sql-functio…

QLora 里的4-bit NormalFloat Quantization中的分位数量化

目录 正态分布的分位数函数详解 1. 正态分布简介 2. 分位数函数定义 3. 正态分布的分位数函数计算 4-bit NormalFloat Quantization 4-bit NormalFloat Quantization详解 1. 4-bit NormalFloat Quantization的定义和应用 2. 4-bit NormalFloat Quantization的工作原理 …

接口自动化测试之-requests模块详解

一、requests背景 Requests 继承了urllib2的所有特性。Requests支持HTTP连接保持和连接池&#xff0c;支持使用cookie保持会话&#xff0c;支持文件上传&#xff0c;支持自动确定响应内容的编码&#xff0c;支持国际化的 URL 和 POST 数据自动编码。 二、requests安装 利用p…

古墓丽影年度版喜加一 亚马逊免费游戏领取教程+下载安装教程

最近我们的老朋友亚马逊平台又为玩家们带来了一款免费的3A大作&#xff0c;这款游戏作为古墓丽影的续作在全球范围内都有着很高的热度和评价。但是许多玩家不知道这款游戏该如何领取&#xff0c;下面小编就为大家带来详细教程。 在领取之前&#xff0c;我们一定要优化我们的网…

Debian mariadb 10.11设定表名 大小写不敏感方法

目录 问题表现&#xff1a;应用中查询 表提示 表不存在 处理步骤&#xff1a; 1、查询表名大小写敏感情况&#xff1a; show global variables like %case%; 2、修改mariadb 配置设置大小写 不敏感 mysql 配置大小写不敏感 mariadb 10.11设置表名大小写不敏感 /etc/mysq…

性能拉满!NeRF与SLAM结合,最新SOTA参数减少30倍!

给大家推荐一个非常新兴的&#xff0c;有大量创新点可以挖掘的好方向&#xff1a;NeRF结合SLAM。 通过结合NeRF的高质量三维场景重建能力和SLAM的动态定位与环境理解能力&#xff0c;我们可以利用逐帧收集的数据&#xff0c;逐渐构建出高质量的3D场景模型&#xff0c;实现更加…

centos无法tab补全至文件

很奇怪的需求&#xff1a;redhat 7.9版本用cd 只能到目录&#xff0c;无法到文件 我个人认为不是个问题&#xff0c;但是甲方需求&#xff0c;你懂的 首先&#xff0c;我们要搞清楚tab补全功能的包bash-completion是否安装&#xff0c;这里肯定是安装了&#xff0c;不过还是看…

引领智算变革,九章云极DataCanvas公司激活油气行业新质生产力

近日&#xff0c;“2024中国石油石化企业信息技术交流大会暨油气产业数字化转型高峰论坛”在京成功举办&#xff0c;九章云极DataCanvas公司携“油气行业AI智算服务全栈解决方案”震撼亮相&#xff0c;为油气行业数智化转型和新质生产力发展提供领先的技术视角和前瞻实战经验分…

热敏电阻符号与常见术语详细解析

热敏电阻是一种电阻器&#xff0c;其特点是电阻值随温度的变化而显著变化&#xff0c;这使得它们成为非常有用的温度传感器。它们可以由单晶、多晶或玻璃、塑料等半导体材料制成&#xff0c;并分为两大类&#xff1a;正温度系数热敏电阻&#xff08;#PTC热敏电阻#&#xff09;和…

2024上半年软考机考新政策:科目连考、分批次考试

辽宁省信息技术教育中心发布了《关于2024年上半年计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试批次安排的通知》。 该通知明确了2024上半年软考辽宁考区的考试时间、考试方式、考试批次安排&#xff0c;与2023下半年软考机考形式有多处调整。 1、考试时间&am…