C++11:列表初始化、新增关键字和新增的默认成员函数

news2025/1/30 16:11:43

目录

一. 列表初始化

1.1 {}列表初始化的方法

1.2 列表初始化实现的原理

二. C++11新增关键字

2.1 decltype -- 自动类型推断

2.2 nullptr -- 空指针

2.3 default -- 强制生成默认成员函数

2.4 delete -- 禁止生成默认成员函数

2.5 final -- 禁止类被继承/禁止虚函数被重写

2.6 override -- 检查子类虚函数是否重写了某个函数

三. C++11新增的两个默认成员函数


一. 列表初始化

1.1 {}列表初始化的方法

在C++98中,可以使用{}来初始化数组、结构体等,在C++11中,列表初始化的用途被扩大到所有的内置类型变量和自定义类型变量,也可以用于在new中给定构造函数的参数。在使用{}初始化时,我们甚至可以不带=。

进入C++11后,所有的STL容器也都会支持列表{}初始化。

代码1.1:列表初始化

int main()
{
	//{}初始化内置类型成员变量
	int i1 = { 1 };
	int i2{ 2 };

	//{}初始化结构体(类对象)

	A a = { 1,3 };
	B b = { 1,2 };

	//{}用于给new出来的对象初值
	B* pb = new B[2]{ {2,4}, {6,8} };
	delete[] pb;

	//STL容器对象列表初始化
	std::vector<int> v = { 1,2,3,4,5 };
	std::list<int> lt = { 1,2,3,4,5 };

	return 0;
}

1.2 列表初始化实现的原理

为了实现列表初始化,C++11单独引入了一个名为initializer_list的类,这个类中包含iterator和const_iterator迭代器成员变量,还包括begin()和end()两个成员函数,用于查找列表的头部和尾部,C++11的STL容器中都会加入列表初始化函数。initializer_list的声明和成员以及部分SLT容器的列表初始化函数声明见图1.1和1.2。

  • typeid(变量名).name() -- 获取变量类型(以字符串的形式表示)

代码1.2:打印initializer_list变量的类型

int main()
{
	auto x = { 1,2,3,4 };
	std::cout << typeid(x).name() << std::endl;   //输出:class std::initializer_list<int>
	return 0;
}

initializer_list的文档:initializer_list - C++ Reference

图1.1 nitializer_list的声明和成员
图1.2 C++11部分STL容器列表构造函数声明

代码1.3为支持列表构造的vector类的简单模拟实现,只需要声明构造函数的参数类型为列表,用迭代器依次遍历链表的每个数据,将其push_back到vector中即可。

代码1.3:支持列表构造的vector类

namespace zhang
{
	template<class T>
	class vector
	{
	public:
		vector()   //默认构造
			: _a(nullptr)
			, _size(0)
			, _capacity(0)
		{ }

		vector(const std::initializer_list<T>& lt)  //列表构造函数
		{
			typename std::initializer_list<T>::const_iterator it = lt.begin();

			while (it != lt.end())
			{
				push_back(*it);
				it++;
			}
		}

		void push_back(const T& x)  //尾插数据函数
		{
			if (_capacity == 0 || _size == _capacity)
			{
				size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;
				_capacity = newCapacity;

				T* tmp = new T[newCapacity];
				memmove(tmp, _a, _size * sizeof(T));
				std::swap(_a, tmp);

				delete[] tmp;
				tmp = nullptr;
			}

			_a[_size] = x;
			++_size;
		}

	private:
		T* _a;
		size_t _size = 0;
		size_t _capacity = 0;
	};
}

二. C++11新增关键字

2.1 decltype -- 自动类型推断

decltype用于自动获取某个数据表达式的类型,可以用decltype获得的类型,去定义新的变量,decltype可以获取变量、字面常量、表示式的类型。

C++11中auto也可以用于自动类型推断,但其与decltype不同的是:auto定义变量时根据所赋的初值确定类型,而decltype在定义新变量时变量类型由传给decltype的数据表达式确定,与所赋初值无关。

代码2.1:decltype自动类型推断

int main()
{
	int i = 10;
	decltype(i) a = 10;
	decltype(i) b = 10.5;  //获取变量类型
	auto c = 10.5;

	std::cout << "a = " << a << std::endl;
	std::cout << "b = " << b << std::endl;
	std::cout << "c = " << c << std::endl;

	decltype(1.55) d = 10.5;   //获取字面常量类型
	decltype(a * d) e = 20.5;  //获取表达式类型

	std::cout << "d = " << d << std::endl;
	std::cout << "e = " << e << std::endl;

	return 0;
}
图2.1 代码2.1的运行结果

2.2 nullptr -- 空指针

在C++98中,NULL被定义为字面常量0,如果用将NULL作为参数传递给函数,那么NULL会被识别为int类型的数据,如代码2.2中,将NULL传给func函数,我们希望运行参数类型为void*形式的重载函数,但实际上运行了int型参数的func,这样就存在调用错误。

为了解决上面的问题,C++11引入了一个新的空指针宏nullptr,用于取代C++98中的NULL,nullptr的定义为:#define nullptr ((void*)0)

如果将nullptr作为参数调用func,则void func(void* x)被正确调用。

代码2.2:NULL和nullptr做参数

void func(void* x) 
{ 
	std::cout << "void func(void* x)" << std::endl; 
}

void func(int x) 
{ 
	std::cout << "void func(int x)" << std::endl; 
}

int main()
{
	func(NULL);
	func(nullptr);
	return 0;
}
图2.2 代码2.2的运行结果

2.3 default -- 强制生成默认成员函数

假设我们在自定义一个类的时候,显示定义了拷贝构造函数,那么编译器就会认为构造函数已经存在,不会再生成默认构造函数。但是,如果我们还是希望编译器生成默认构造函数,可以使用default强制生成,default强制生成默认成员函数语法为:函数声明 = default。

代码2.3:default强制生成成员函数

class A
{
public:
	A() = default;   //强制生成默认构造

	A(const A& a)
		: _a1(a._a1)
		, _a2(a._a2)
	{ }

	A& operator=(const A& a) = default;  //强制生成拷贝构造函数

	A& operator=(A&& a)  //移动拷贝
	{
		_a1 = a._a1;
		_a2 = a._a2;
		return *this;
	}

private:
	int _a1  =10;
	int _a2 = 20;
};

2.4 delete -- 禁止生成默认成员函数

如果我们希望定义一个不允许被赋值(operator=)的类,在C++98的标准下,我们需要定义一个继承体系,将基类的赋值运算符重载函数声明为私有类型,然后将禁止被赋值的类定义为这个基类的派生类。这样,如果我们试图赋值,程序会报错。

代码2.4:通过定义继承体系禁止类对象被赋值(C++98)

class Forbid
{
private:
	Forbid& operator=(const Forbid& f) { };
};

//Derive继承Forbid类,Forbid类中的operator=函数为私有
//Derive类对象不能被赋值
class Derive: public Forbid
{
public:
	Derive(int x = 10, int y = 20)
		: Forbid()
		, _x(x)
		, _y(y)
	{ }

private:
	int _x;
	int _y;
};

int main()
{
	Derive d1;
	Derive d2(20, 30);
	d1 = d2;  //编译报错
	return 0;
}

但是C++98通过继承禁止拷贝赋值十分复杂繁琐,且实际上并没有让operator=函数消失,只是由于父类的operator=函数被声明为了私有,无法被调用而已。

为此,C++11赋予了关键字delete新的用途 -- 禁止生成某个默认成员函数。

语法:函数声明 = delete

代码2.5:delete禁止特定成员函数的生成

class A
{
public:
	A(int a1 = 10, int a2 = 20)
		: _a1(a1)
		, _a2(a2)
	{ }

	//禁止生成拷贝构造和赋值运算符重载函数
	A(const A& a) = delete;
	A& operator=(const A& a) = delete;

private:
	int _a1 = 10;
	int _a2 = 20;
};

2.5 final -- 禁止类被继承/禁止虚函数被重写

final关键字的两种作用:

  • 声明某个类不能被继承
  • 声明某个虚函数不能被重写

代码2.6:final关键字的使用

//Base类不能被继承
class A final
{ };

class B: public A  //报错
{ };

class AA
{
public:
	virtual void func() final { std::cout << "class AA" << std::endl; }
};

class BB : public AA
{
public:
	//非法重写func函数,报错
	virtual void func() { std::cout << "class BB" << std::endl; }
};

2.6 override -- 检查子类虚函数是否重写了某个函数

override关键字:放在子类中虚函数声明的后面,如果子类虚函数没有重写某个父类的虚函数,那么就编译报错。

代码2.7:override检查子类虚函数重写

class AA
{
public:
	//virtual void func() { std::cout << "class AA" << std::endl; }
};

class BB : public AA
{
public:
	//func为重写父类虚函数,报错
	virtual void func() override { std::cout << "class BB" << std::endl; }
};

int main()
{
	BB bb;
	return 0;
}

三. C++11新增的两个默认成员函数

在C++98中,我们认为编译器自动生成的默认构造函数有6个,分别为:构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、对于普通对象的取地址运算符重载函数、对于const对象的的取地址运算符重载函数。

图3.1 C++11的8个默认成员函数

由于C++11新增了右值引用,为了减少拷贝的消耗,C++11中默认成员函数增加到了8个,新增的两个为:移动构造函数、移动赋值函数。

  • 编译器自动生成移动构造函数的条件:不显示定义移动构造函数、不显示定义拷贝构造、赋值运算符重载、析构三个默认成员函数的任意一个。
  • 编译器自动生成的移动构造函数进行的工作:对于内置类型成员变量进行浅拷贝,对于自定义类型成员变量,如果有移动构造函数,调用其移动构造函数,如果没有,调用其拷贝构造函数。
  • 移动赋值函数自动生成的条件与移动赋值函数相同,进行的工作与移动构造函数基本一致,内置类型值拷贝,自定义类型调用其拷贝赋值函数或移动赋值函数。

为了观察默认生成的移动构造函数进行的工作,编写代码3.1,类BB中包含类AA的自定义类型成员变量,将BB类的拷贝构造、拷贝赋值和析构函数全部注释掉,然后在主函数中利用拷贝构造和移动构造创建对象dd2和dd3,运行代码(结果见图3.2),可以观察到aa的移动构造函数被bb默认生成的移动构造函数调用了。

注意:由于VS2013不能很好的支持C++11的一些新特性,应当用VS2019演示代码3.1。

代码3.1:默认生成移动构造函数的功能验证

class AA
{
public:
	AA(int a1 = 1)
		: _a1(a1)
	{ }

	AA(const AA& aa)
		: _a1(aa._a1)
	{  
		std::cout << "拷贝构造 -- AA" << std::endl;
	}

	AA(AA&& aa)
		: _a1(aa._a1)
	{
		std::cout << "移动构造 -- AA" << std::endl;
	}

private:
	int _a1 = 1;
};

class BB
{
public:
	BB(int aa = 1, int b1 = 2)
		: _aa(AA(1))
		, _b1(b1)
	{ }

	/*BB(const BB& bb)
		: _aa(bb._aa)
		, _b1(bb._b1)
	{ }*/

	/*BB& operator=(const BB& bb)
	{
		_b1 = bb._b1;
	}*/

	/*~BB(){ }*/

private:
	AA _aa;
	int _b1;
};

int main()
{
	BB bb1(10, 20);
	BB bb2(bb1);
	BB bb3(std::move(bb1));

	return 0;
}
图3.2 代码3.1的运行结果

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

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

相关文章

自动化测试之PageObject设计模式

译文如下&#xff1a; PageObject 当您针对网页编写测试时&#xff0c;您需要参考该网页中的元素以单击链接并确定显示的内容。但是&#xff0c;如果您编写直接操作 HTML 元素的测试用例&#xff0c;则您的测试将无法应对 UI 中的频繁修改。PageObject对应于一个HTML网页、页…

5月26号软件资讯更新合集......

Windows Terminal 1.18 新功能预览&#xff1a;标签拖拽、上下文菜单... Windows Terminal 预览版已更新到 1.18 版本&#xff0c;带来多项实用内容&#xff0c;一起来看看这个版本的新东西&#xff1a; 标签撕裂&#xff08;拖拽功能&#xff09; Windows Terminal 已支持对…

安装Redis6

安装Redis 安装redis依赖 redis是基于C语言编写的&#xff0c;因此首选需要安装redis所需的gcc依赖 yum install -y gcc tcl 上传安装包并解压 我安装的是redis-6.2.6版本&#xff0c;并且放到了/usr/local/src目录下 - 进入/usr/local/src目录shellcd /usr/local/src解压 tar …

全球最受欢迎低代码平台排行榜出炉

低代码平台正在成为寻求快速有效地构建应用程序的企业的首选解决方案。这些平台减少了编码要求&#xff0c;使企业能够在降低成本的同时更快地完成应用程序开发项目。在本文中&#xff0c;将探索全球受欢迎的低代码平台排行榜。 该排名使用的标准包括易用性、成本效益、集成性、…

很后悔,才发现这个API管理神器

想必大家都注意到了&#xff0c;近半年国产API管理工具火了起来。这说明两个问题&#xff0c;第一&#xff0c;API管理的重要性被越来越多的开发者认识到了&#xff0c;研发团队对API管理的需求也越来越强了。第二&#xff0c;说明国产软件真是越来越厉害了&#xff0c;大家确实…

《微服务实战》 第十八章 Redis查看配置文件和数据类型

前言 本章节讲解如何查看、修改Redis配置&#xff0c;介绍Redis类型。 1、查看配置 config get 配置名称 2、修改配置项 config set 配置项名称 配置项值 2.1、配置项说明 配置项参数说明daemonizeno/yes默认为 no&#xff0c;表示 Redis 不是以守护进程的方式运行&#xff…

论C站如何获得铁粉?过来人给出几点建议

哈喽&#xff0c;我是bug菌&#xff0c;一名想走&#x1f463;出大山改变命运的程序猿。周五啦&#xff0c;刚肝完需求的我&#xff0c;闲暇之时逛C站热榜&#xff0c;偶然刷到一条看到官方抛出的话题&#xff1a;"在C站如何获得铁粉&#xff1f;"&#xff0c;我寻思…

操作系统第五章——输入输出管理(上)

提示&#xff1a;初入红尘&#xff0c;不知人间疾苦&#xff0c;蓦然回首&#xff0c;已是苦中之人&#xff0c;这杯中酒三分&#xff0c;这酒中悲七分。关关难过关关过&#xff0c;夜夜难熬夜夜熬&#xff0c;愿这人世间所有爱恨情仇皆溶于酒&#xff0c;且将这红尘做酒&#…

MP4如何让去水印?python带你实现~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 开发环境: 解释器版本: python 3.8 代码编辑器: pycharm 2021.2 模块使用: 内置模块&#xff08;无需安装&#xff09; os —> python系统编程的操作模块,提供了非常丰富的功能去处理文件和目录 sys —> 是与…

我是00后,我卷一点怎么了?

前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作都还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff0c;所以…

手动创建django项目和python虚拟环境

在使用pycharm创建django项目的时候&#xff0c;报错如下&#xff1a; C:\Users\12051\AppData\Local\Temp\tmplkz609ucpycharm-management\setuptools-40.8.0\setup.py install Traceback (most recent call last):File "C:\Users\12051\AppData\Local\Temp\tmpqphl…

合并两个有序链表(java)

leetcode 21题&#xff1a;合并两个有序链表 题目描述解题思路&#xff1a;链表的其它题型。 题目描述 leetcode21题&#xff1a;合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例&#xff1a; 输入&…

IO多路转接

目录 一、select 1.1 select初识 1.2 select函数 1.3 scoket就绪条件 1.4 select基本工作流程 1.5 select服务器 1.6 select的优点 1.7 select的缺点 1.8 select的适用场景 二、poll 2.1 poll函数 2.2 poll服务器 2.3 poll的优点 && 缺点 三、epoll 3.1…

设备快线客户端软件V1.0用户手册

1.前言欢迎使用设备快线客户端软件产品。设备快线客户端软件简称DYClient,DYClient客户端是东用科技有限公司推出的一款用于远程维护的控制软件&#xff0c;主要为客户远程访问现场终端设备提供便捷的接入服务&#xff0c;并且通过DYClient客户端软件用户可以非常方便快捷的访问…

ChatGPT和软件测试实践与思考

前言 关于最近大火的ChatGPT相信各位也听过不同渠道听说过他的厉害&#xff0c;目前发展趋势比较火热&#xff0c;科技公司都有在考虑怎么使用ChatGPT进行提高研发效率以及办公效率&#xff0c;最近我所在的公司也有在要求大家使用ChatGPT进行改善工作效率&#xff0c;所以引发…

支持导入 Eolink 插件,别小看这个开源 API 管理工具了

Postcat 有多达 30 款支持数据迁移、主题、API 安全等方面的插件。 导入 Eolink 插件。 使用 导入功能有多个入口&#xff0c;你可以在 API 分组处点击加号导入 API&#xff1a; 也可以换种方式&#xff0c;在首页里导入Eolink 如果你日常会用到 api 管理工具的话&#xff0c…

【九章斩题录】C/C++:二维数组中的查找(JZ4)

精品题解 &#x1f449; 《九章刷题录》 &#x1f4dc; 目录&#xff1a; 「 法一 」暴力美学 「 法二 」十字分割法 「 法三 」逐行二分 JZ4 - 二维数组中的查找 &#x1f4da; 题目描述&#xff1a;在一个二维数组 array 中&#xff08;每个一维数组的长度相同&#xff…

[时间同步] vscode chatGPT提供的程序打包封装成api解决方案怎么样

背景 在完成gnss时间同步程序大部分需求串口配置、串口数据中找出推荐定位信息RMC解析UTC时间以及UTC时间更新系统时间等功能后&#xff0c;有个需求比较特别&#xff0c;需要客户来操作。当车辆在地库场景待时间过久后重新回到地面&#xff0c;一直在自走的系统时间与又定位好…

Three.js--》探索Three.js:学习和就业的完整指南

目录 three.js的学习建议 WebGL前端工程师工作待遇相关问题 本篇文章主要给大家介绍一下如何学习Web3D可视化&#xff0c;具体说就是怎么学习WebGL、Three.js、3D建模等。 three.js的学习建议 在过去互联网是人联网的时代&#xff0c;开发人和人之间的联系的Web应用&#x…

MoveIt2中使用trac_ik

文章目录 1.下载trac_ik的源码2.安装 NLopt library3.编译源码4.使用 在ros1moveit1中&#xff0c;使用trac_ik是很简单的一件事情&#xff1a;【TRAC-IK Kinematics Solver】 但是在Ros2中&#xff0c;无论MoveIt2也好&#xff0c;还是trac_ik也好&#xff0c;都没有提供标准的…