C++11之新的类功能

news2024/12/24 10:00:15

这里写目录标题

  • 新的类功能
    • 默认成员函数
    • 类成员变量初始化
    • 强制生成默认函数的关键字default
    • 禁止生成默认函数的关键字delete
    • final与override关键字
    • override

新的类功能

默认成员函数

原来C++类中,有6个默认成员函数:

  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值重载
  5. 取地址重载
  6. const 取地址重载

C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

  • 如果你没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

默认生成的移动构造和移动赋值会做什么?

  • 默认生成的移动构造:对于内置类型的成员,编译器会完成值拷贝(浅拷贝),对于自定义类型的成员,如果它实现了移动构造函数,就会调用他的移动构造函数,如果没有实现,就会调用它的拷贝构造函数。
  • 默认生成的移动赋值:对于内置类型的成员,编译器会完成值拷贝(浅拷贝),对于自定义类型的成员,如果它实现了移动赋值,就会调用他的移动赋值,如果没有实现,就会调用它的拷贝赋值。

下面我们用一个string类和一个Person类来更加深层的理解一下移动构造和移动赋值:

namespace gtt
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			//cout << "string(char* str)" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// s1.swap(s2)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
				cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		// 移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动语义" << endl;
			swap(s);
		}
		// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动语义" << endl;
			swap(s);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}
class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}

	//构造函数
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}

	//赋值重载
	Person& operator=(const Person& p)
	{
		if (this != &p)
		{
			_name = p._name;
			_age = p._age;
		}
		return *this;
	}

	//析构函数
	~Person()
	{}
private:
	gtt::string _name;
	int _age;
};

虽然Person类并没有实现移动构造和移动赋值,但是拷贝构造,拷贝赋值,析构函数都实现,此时在调用的过程中就不会在默认生成移动构造和移动赋值,我们看下面这段代码:

int main()
{
	Person s1("张三", 18);

	//想要调用Person的移动构造函数
	Person s2 = move(s1);
	return 0;
}

上述代码中,Person类并没有实现移动构造和移动赋值,此时就会调用我们自己实现的拷贝构造函数,会对我们的s1对象的name成员进行深拷贝,也就会调用string类的拷贝构造函数对name成员进行深拷贝。

在这里插入图片描述
但是如果我们将拷贝构造,拷贝赋值,析构函数都注释掉,此时Person类就会默认生成移动构造和移动赋值,就会调用他默认生成的移动构造和移动赋值,对于age这种内置类型就完成值拷贝,对于name这种自定义类型因为我们已经实现了他的移动构造和移动赋值,就会去调用他的移动构造和移动赋值。
在这里插入图片描述

类成员变量初始化

默认生成的构造函数,对于自定义类型的成员会调用其构造函数进行初始化,但并不会对内置类型的成员进行处理。于是C++11支持非静态成员变量在声明时进行初始化赋值,默认生成的构造函数会使用这些缺省值对成员进行初始化。

class Person
{
public:
	//....
private:
	gtt::string _name = "张三";
	int _age = 21;
};

强制生成默认函数的关键字default

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}

	//拷贝构造函数
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}
private:
	gtt::string _name = "张三";
	int _age = 21;
};

int main()
{
	Person s1("张三", 18);

	Person s2 = move(s1);
	return 0;
}

最终结果还是会调用我们移动构造函数。

再比如我们自己已经写看拷贝构造函数,此时就不会在生成默认构造函数,我们也可以强制生成:

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

	//拷贝构造函数
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}
private:
	gtt::string _name = "张三";
	int _age = 21;
};

int main()
{
	Person s;
	return 0;
}

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

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(Person&& p) = delete;
private:
	gtt::string _name = "张三";
	int _age = 21;
};

final与override关键字

final修饰类

被final修饰的类叫做最终类,最终类无法被继承:

class A final
{
public:
	//....
private:
	int a = 1;
};

class B : public A //无法继承A,因为被final修饰
{
public:
	//....
private:
	int b = 2;
};

在这里插入图片描述

final修饰虚函数

final修饰虚函数,表示该虚函数不能再被重写,如果子类继承后重写了该虚函数则编译报错:

class A 
{
public:
	virtual void func() final
	{
		cout << "hello A" << endl;
	}
private:
	int a = 1;
};

class B : public A
{
public:
	virtual void func()
	{
		cout << "hello B" << endl;
	}
private:
	int b = 2;
};

在这里插入图片描述

override

override修饰虚函数

override修饰子类的虚函数,检查子类是否重写了父类的某个虚函数,如果没有没有重写则编译报错。

class A
{
public:
	void func()
	{
		cout << "hello A" << endl;
	}
private:
	int a = 1;
};

class B : public A
{
public:
	virtual void func() override
	{
		cout << "hello B" << endl;
	}
private:
	int b = 2;
};

在这里插入图片描述

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

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

相关文章

Spring学习笔记13 Spring对事务的支持

Spring学习笔记12 面向切面编程AOP-CSDN博客 什么是事务:在一个业务流程当中,通常需要多条DML(insert delete update)语句共同联合才能完成,这多条DML语句必须同时成功,或者同时失败,这样才能保证数据的安全. 多条DML要么同时成功,要么同时失败,叫做事务(Transaction) 事务四…

安防视频平台EasyCVR视频调阅全屏播放显示异常是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

Apache DolphinScheduler在中国信通院“2023 OSCAR开源尖峰案例”评选中荣获「尖峰开源项目奖」!

在近日由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;和中国通信标准化协会联合主办的“2023 OSCAR 开源产业大会”上&#xff0c;主办方公布了 2023 年“OSCAR 开源尖峰案例”评选结果&#xff0c;包括“开源人物”“开源项目”“开源社区”“开源企业”…

python+vue实验室课程预约管理系统

实验室课程管理系统运用计算机完成数据收集、查询、修改和删除以及统计等工作&#xff0c;提高了管理者工作效率&#xff0c;避免了因信息量巨大&#xff0c;造成的人为错误&#xff0e;通过前面的功能分析可以将实验室课程管理系统的功能分为管理员、学生和教师三个部分&#…

Linux高性能服务器编程 学习笔记 第八章 高性能服务器程序框架

TCP/IP协议在设计和实现上没有客户端和服务器的概念&#xff0c;在通信过程中所有机器都是对等的。但由于资源&#xff08;视频、新闻、软件等&#xff09;被数据提供者所垄断&#xff0c;所以几乎所有网络应用程序都采用了下图所示的C/S&#xff08;客户端/服务器&#xff09;…

LeetCode_BFS_中等_1926.迷宫中离入口最近的出口

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一个 m x n 的迷宫矩阵 maze &#xff08;下标从 0 开始&#xff09;&#xff0c;矩阵中有空格子&#xff08;用 ‘.’ 表示&#xff09;和墙&#xff08;用 ‘’ 表示&#xff09;。同时给你迷宫的入口 …

电脑提示vcruntime140.dll缺失重新安装的修复方法

电脑出现 vcruntime140.dll 丢失的情况&#xff0c;通常是由于系统缺失了 Microsoft Visual C Redistributable 的运行库文件。这个文件是许多应用程序在运行时所需的依赖库&#xff0c;如果丢失了该文件&#xff0c;可能会导致某些软件无法正常运行。 下面是关于 vcruntime140…

免费录音软件推荐,告别杂音,音质更清晰!

“求推荐一款免费的录音软件&#xff01;最近下载了好多的录音软件&#xff0c;不是音质太差&#xff0c;就是需要收费解锁新的功能&#xff0c;根本不好用&#xff0c;有没有人知道一款免费优秀的录音软件呀&#xff0c;告诉我一下。” 录音已成为现代人们学习和工作中的一项…

DataExcel控件读取和保存excel xlsx 格式文件

需要引用NPOI库 https://github.com/dotnetcore/NPOI 调用Read 函数将excel读取到dataexcel控件 调用Save 函数将dataexcel控件文件保存为excel文件 using NPOI.HSSF.UserModel; using NPOI.HSSF.Util; using NPOI.SS.UserModel; using NPOI.SS.Util; using System; using …

pytho实例--pandas读取表格内容

前言&#xff1a;由于运维反馈帮忙计算云主机的费用&#xff0c;特编写此脚本进行运算 如图&#xff0c;有如下excel数据 计算过程中需用到数据库中的数据&#xff0c;故封装了一个读取数据库的类 import MySQLdb from sshtunnel import SSHTunnelForwarderclass SSHMySQL(ob…

Java BigDecimal 详解

目录 一、BigDecimal 1、简介 2、构造器描述 3、方法描述 4、使用 一、BigDecimal float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&#xff0c;它…

25841-2017 1000kV电力系统继电保护技术导则

声明 本文是学习GB-T 25841-2017 1000kV电力系统继电保护技术导则. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了交流1000 kV 系统及1000 kV 变电站相关电压等级具有特别要求的继电保护装置 的基本准则。 本标准适用于1000 k…

大厂秋招真题【BFS+DP】华为20230921秋招T3-PCB印刷电路板布线【欧弟算法】全网最全大厂秋招题解

题目描述与示例 题目描述 在PCB印刷电路板设计中&#xff0c;器件之间的连线&#xff0c;要避免线路的阻抗值增大&#xff0c;而且器件之间还有别的器任和别的干扰源&#xff0c;在布线时我们希望受到的干扰尽量小。 现将电路板简化成一个M N的矩阵&#xff0c;每个位置&am…

如何快速学习AdsPower RPA(1)——简单、进阶部分

你是否刚开始学习使用AdsPower的RPA功能&#xff1f; 你是否对着这些操作选项头皮发麻&#xff0c;不知所措&#xff1f; 你是否想快速学会RPA&#xff1f; 你是否想编写出满足各种业务场景的RPA流程&#xff1f; 以上这些&#xff0c;Tool哥统统都帮你搞定&#xff01; Too…

科技成果鉴定测试有多重要?可出具专业测试报告的软件测评机构推荐

科技成果鉴定测试在现代社会中具有重要意义&#xff0c;它不仅可以评估科技成果的价值和可行性&#xff0c;还可以为科技创新提供决策依据&#xff0c;推动科技进步和社会发展&#xff0c;那么科技成果鉴定测试究竟重要在哪呢? 1、对于科技项目的投资决策至关重要。鉴定测试可…

YOLOV8-DET转ONNX和RKNN

目录 1. 前言 2.环境配置 (1) RK3588开发板Python环境 (2) PC转onnx和rknn的环境 3.PT模型转onnx 4. ONNX模型转RKNN 6.测试结果 1. 前言 yolov8就不介绍了&#xff0c;详细的请见YOLOV8详细对比&#xff0c;本文章注重实际的使用&#xff0c;从拿到yolov8的pt检测模型&…

玩转gpgpu-sim 04记—— __cudaRegisterBinary() of gpgpu-sim 到底做了什么

官方文档&#xff1a; GPGPU-Sim 3.x Manual __cudaRegisterBinary(void*) 被执行到的代码逻辑如下&#xff1a; void** CUDARTAPI __cudaRegisterFatBinary( void *fatCubin ) { #if (CUDART_VERSION < 2010)printf("GPGPU-Sim PTX: ERROR ** this version of GPGPU…

查看Linux系统信息的常用命令

文章目录 1. 机器配置查看2. 常用分析工具3. 常用指令解读3.1 lscpu 4. 定位僵尸进程5. 参考 1. 机器配置查看 # 总核数物理CPU个数x每颗物理CPU的核数 # 总逻辑CPU数物理CPU个数x每颗物理CPU的核数x超线程数 cat /proc/cpuinfo| grep "physical id"| sort| uniq| w…

电子电路学习笔记之NCV6324BMTAATBG——同步降压转换器

关于同步降压转换器&#xff1f; 是一种广泛应用于各种电子产品中的转换器。它具有输入范围宽、转换效率高、输出电流大等优点。在同步降压型转换器的驱动电路中&#xff0c;一般包括电平移位电路、死区时间控制电路以及过零检测电路等。 电平移位电路用于将固定电平Vcc和GND…

86、Redis 的 value 所支持的数据类型(String、List、Set、Zset、Hash)---->String相关命令

本次讲解要点&#xff1a; String相关命令&#xff1a;String是指value中的数据类型 启动redis服务器&#xff1a; 打开小黑窗&#xff1a; C:\Users\JH>e: E:>cd E:\install\Redis6.0\Redis-x64-6.0.14\bin E:\install\Redis6.0\Redis-x64-6.0.14\bin>redis-server.…