【1++的C++进阶】之C++11(二)

2023-09-13 21:42:17

👍作者主页:进击的1++
🤩 专栏链接:【1++的C++进阶】

一,类的新变化

在C++03之前,我们的默认成员函数有6个,我们在类与对象这篇中有过详细的讲解。C++11中又增加了两个默认成员函数—移动构造与移动赋值重载,其底层原理以及优势我们在上节已经有过描述。但是针对这两个默认成员函数,我们还需要注意以下说明:

  1. 如果你没有实现移动构造函数,并且也没有实现析构函数,拷贝构造,拷贝赋值重载中的任意一个,那么,编译器将会生成一个默认的移动构造函数,默认生成的移动构造对于内置类型成员,会进行逐字节的拷贝,对于自定义类型成员则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。移动赋值重载与移动构造此种情况类似。
  2. 如果你提供了移动构造或者移动赋值重载,那么编译器将不会再提供拷贝构造和拷贝赋值重载。

我们以以下代码为例:

class string
	{
	public:
		string(const char* str = "")
			:_str(nullptr)
		{
			cout << "string(const char* str)" << endl;
		}

		//拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			string tmp(s._str);
			std::swap(_str, tmp._str);
			//....
			cout << "string(const string& s)" << endl;
		}

		//移动构造
		string(string&& s)
		{
			std::swap(s._str, _str);
			cout << "string(string&& s)" << endl;

		}

		//赋值重载
		string& operator=(string& s)
		{

			std::swap(s._str, _str);
			cout << "string& operator=(string& s)" << endl;
			return *this;

		}

		//移动赋值
		string& operator=(string&& s)
		{
			std::swap(s._str, _str);
			return *this;
		}


	private:
		char* _str;
	};

	template<class T>
	class A
	{
	public:
		A(T&& s)
			:_a(0)
			,_s(std::forward<T>(s))
		{
			cout << "A" << endl;
		}
		
	A(T& s)
				:_a(0)
				, _s(s)
			{
				cout << "A" << endl;
			}
	private:
		int _a;
		T _s;
	};

	void test1()
	{
		hyp::string s2 = ("234");
		A<string> a3(s2);
		A<string> a4(move(a3));
	}

在这里插入图片描述

通过上述结果我们可以发现,对于自定义成员,其在没有自己实现析构函数,拷贝构造,赋值重载时,会自动调用自定义成员的移动构造。
当我们在类A中自己实现析构函数,拷贝构造,赋值重载任意一个时,结果如下:
在这里插入图片描述
其就不再自动调用自定义类型成员的移动构造,而是调用拷贝构造。
在这里插入图片描述
当我们添加A的移动构造后,编译器便不会再生成拷贝构造和赋值重载,而且我们也没有写,编译器便会报错。

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化。

强制生成默认函数的关键字default:
在这里插入图片描述
当有了移动构造后,便不会再生成拷贝构造,因此我们可以使用default当强制生成拷贝构造。

禁止生成默认函数的关键字delete:

二,可变参数模板

C++11的新特性可变参数模板能够让我们创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧。
我们在这里只进行简单的了解,下面我将演示两种能够获取到参数包中参数的方法。

方法一—递归

template<class T>
void Showlist(const T val)//递归终止条件
{
	cout << val << endl;
}

template<class T,class ...Args>
void Showlist(T val, Args... args)
{
	cout << val << " ";
	Showlist(args...);

}

方法二–逗号表达式

template<class T>
void printargs(T t)
{
	cout << t << " ";
}

template<class ...Args>
void Getargs(Args ...args)
{
	int arr[] = { (printargs(args),0)... };
}

在这里插入图片描述
在这里插入图片描述

三,lambda表达式

为什么要有lambda表达式?
假设我们现在需要对一个集合进行排序,(我们用std::sort进行排序)当我们要排升序时则需要传一个升序规则的仿函数,要降序时,则传一个降序规则的仿函数,当要元素类型不同时,则又需要该这个仿函数。比较麻烦,而lambda表达式可以避免这个麻烦,因此在C++11中就有了lambda表达式的出现。

lambda表达式的格式:
[捕捉列表] (参数列表) mutable -> 返回值类型 { 函数体}。

捕捉列表: 该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
参数列表: 与普通函数的参数列表一致,如果不需要参数传递,则可以
连同()一起省略。
mutable: 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
返回值类型: 用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
函数体: 在该函数体内,除了可以使用其参数外,还可以使用所有捕获
到的变量。

我们来小总结一下:在lambda表达式中,参数列表,返回值类型,mutable是可以选择的。因此我们就有了一个最简单的lambda表达式:[ ]{}。但该lambda表达式不能做任何事情。

关于捕获列表:
捕捉列表描述了上下文中哪些变量能够被lambda,是传值使用还是引用使用。
[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
[&var]:表示引用传递捕捉变量var
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针

在这里插入图片描述
lambda表达式之间是不能够相互赋值的,但是可以进行拷贝构造,可以将其赋值给一个相同类型的函数指针。
在这里插入图片描述
在这里插入图片描述
明明是一样的两个lambda表达式,为什么却显式不能赋值呢?
我们会在后面进行说明。

可以像函数一样使用的对象有三种:函数指针;仿函数,又叫函数对象;lambda表达式。

我们以以下代码为例:

void test5()
{
	int val = 5;
	Test t(val);
	t(val);
	auto ret = [=](int tt) {return tt + val; };
	ret(val);
	cout << ret(val) << endl;
	cout << t(val) << endl;

}

在这里插入图片描述
我们再观察其汇编代码。
在这里插入图片描述
通过观察我们发现仿函数先是会调用其构造函数,构造出一个对象。
lambda表达式也通过捕获列表将捕获到的值用于初始化会,构造出一个对象。每一个lambda构造出的对象都是不同的,因此其看似两个相同的lambda,却不能够赋值。
在这里插入图片描述
在这里插入图片描述
并且,接下来他们都调用了operator()!!!!
因此实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

更多推荐

贝叶斯分位数回归、lasso和自适应lasso贝叶斯分位数回归分析免疫球蛋白、前列腺癌数据...

原文链接:http://tecdat.cn/?p=22702贝叶斯回归分位数在最近的文献中受到广泛关注,本文实现了贝叶斯系数估计和回归分位数(RQ)中的变量选择,带有lasso和自适应lasso惩罚的贝叶斯(点击文末“阅读原文”获取完整代码数据)。摘要还包括总结结果、绘制路径图、后验直方图、自相关图和绘制分位数图的进一

数据库开发-MySQL基础DQL和多表设计

1.数据库操作-DQLDQL英文全称是DataQueryLanguage(数据查询语言),用来查询数据库表中的记录。1.1介绍查询关键字:SELECT查询操作是所有SQL语句当中最为常见,也是最为重要的操作。在一个正常的业务系统中,查询操作的使用频次是要远高于增删改操作的。当我们打开某个网站或APP所看到的展示信息,都

微信小程序案例2-2:本地生活

文章目录一、运行效果二、知识储备(一)swiper与swiper-item组件1、swiper组件(1)功能描述(2)属性说明2、swiper-item组件(1)功能描述(2)属性说明3、基本语法4、案例演示(二)text组件1、常用属性2、案例演示(三)Flex布局1、什么是Flex布局2、基本概念(1)Flex容器

【含面试】解锁MySQL group_concat的无限可能性:解决长度限制并实现高效查询

AI绘画关于SD,MJ,GPT,SDXL百科全书面试题分享点我直达2023Python面试题2023最新面试合集链接2023大厂面试题PDF面试题PDF版本java、python面试题项目实战:AI文本OCR识别最佳实践AIGamma一键生成PPT工具直达链接玩转cloudStudio在线编码神器玩转GPUAI绘画、A

Python日期处理库:掌握时间的艺术

💂个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】日期和时间在计算机编程中起着至关重要的作用,无论您是在开发应用程序、分析数据还是进行自动化任务,都需要处理日期和时间。Python作为一门

vue-h5移动Web的Flex布局

Flex布局Flexible布局,也就是弹性布局。Flexible的优点是,不需要对元素设置固定的宽度和高度,元素的位置和大小也会跟着父元素或者浏览器的状态来自动适配。同时还添加了水平居中和垂直居中的解决方案。在页面中指定一个元素作为Flex布局,那么这个元素就是作为容器冤元素。设置如下:.box{display:fl

Vuex详解:Vue.js的状态管理方案

🌷🍁博主猫头虎(🐅🐾)带您GotoNewWorld✨🍁🦄博客首页——🐅🐾猫头虎的博客🎐🐳《面试题大全专栏》🦕文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》🐾学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》🐅学会Gol

从数字化到智能化再到智慧化,智慧公厕让城市基础配套更“聪明”

随着科技的迅猛发展,城市生活方式与配置设施的方方面,面也在不断的改变和升级。智慧公厕作为城市基础配套设施的一部分,从数字化到智能化再到智慧化,正逐渐展现出其独特的魅力和优势。实现了公共厕所建设、使用与管理方式的全面变革,本文将以智慧公厕领航厂家广州中期科技有限公司,大量的精品案例项目的实景实例实图,深入探讨智慧公厕的定

CSS 之 grid 网格布局

一、简介​display:grid;用于设置元素内部的布局类型为网格布局,其外显类型为块级元素。该类型的元素将内部分为行和列,划分成一个个单元格,并通过一系列相关属性控制单元格及其内容的布局和大小。​该属性值的主要应用场景为:多行多列元素排列布局。采用grid布局的元素,被称为“容器”(container),其内部的直

Maven 使用

一、初始Maven(了解)1、项目遇到的问题构建:编译代码,运行测试,打包,部署应用,运行服务器等;依赖:项目依赖大量的第三方包,第三方包又依赖另外的包,对依赖包的管理非常麻烦。2、Maven定义和作用Maven翻译为“知识的积累”,“专家”,“行家”,是一个跨平台的项目管理工具;Maven主要用作基于Java平台的项

用于图像分类的预训练模型(PyTorch实现)

用于图像分类的预训练模型(PyTorch实现)在本文中,我们将介绍一些使用TorchVision模块中存在的预训练网络的实践示例——用于图像分类的预训练模型。1.基于预训练模型进行图像分类预训练模型是在ImageNet等大型基准数据集上训练的神经网络模型。深度学习社区从这些开源模型中受益匪浅。此外,预训练模型是计算机视

热文推荐