类的新功能

类的新功能

默认成员函数

在C++11之前,一个类中有如下六个默认成员函数:

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

其中前四个默认成员函数最重要,后面两个默认成员函数一般不会用到,这里默认成员函数是我们不写编译器会自动生成的函数。在C++11标准中又增加了两个默认成员函数,分别是移动构造函数和移动赋值重载函数。

移动构造函数的生成条件:自己没有实现移动构造函数,并且自己没有实现析构函数拷贝构造函数赋值重载函数
移动赋值重载函数的生成条件:自己没有实现移动赋值重载函数,并且自己没有实现析构函数拷贝构造函数赋值重载函数

因此我们需要注意的是:移动构造和移动赋值的生成条件与之前六个默认成员函数不同,并不是单纯的没有实现移动构造和移动赋值,编译器就会默认生成

默认生成的移动构造函数:对于内置类型的成员会完成值拷贝(浅拷贝),对于自定义类型的成员,如果该成员实现了移动构造就调用它的移动构造,否则就调用它的拷贝构造。
默认生成的移动赋值重载函数:对于内置类型的成员会完成值拷贝(浅拷贝),对于自定义类型的成员,如果该成员实现了移动赋值就调用它的移动赋值,否则就调用它的赋值重载。

我们实现一个简化版的string来验证默认生成的移动构造和移动赋值重载函数的功能实现。

namespace a
{
	class string
	{
	public:
		//构造函数
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//交换两个对象的数据
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//拷贝构造函数(现代写法)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;

			string tmp(s._str);
			swap(tmp);
		}
		//移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		//赋值重载函数(现代写法)
		string& operator=(const string& s)
		{
			cout << "string& operator=(const string& s) -- 深拷贝" << endl;

			string tmp(s);
			swap(tmp);
			return *this;
		}
		//移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}
		//析构函数
		~string()
		{
			//delete[] _str;
			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

然后我们再编写一个简单的Person类

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:
	a::string _name;
	int _age; 
};

虽然Person类当中没有实现移动构造和移动赋值,但拷贝构造、拷贝赋值和析构函数Person类都实现了,因此Person类中不会生成默认的移动构造和移动赋值,可以通过下面的代码来验证:

int main()
{
	Person s1("张三", 21);
	Person s2 = move(s1); //想要调用Person默认生成的移动构造

	return 0;
}

上述代码中用一个右值去构造s2对象,但由于Person类没有生成默认的移动构造函数,因此这里会调用Person的拷贝构造函数(拷贝构造既能接收左值也能接收右值),这时在Person的拷贝构造函数中就会调用string的拷贝构造函数对name成员进行深拷贝。

如果要让Person类生成默认的移动构造函数,就必须将Person类中的拷贝构造、拷贝赋值和析构函数全部注释掉,这时用右值去构造s2对象时就会调用Person默认生成的移动构造函数。

  • Person默认生成的移动构造,对于内置类型成员age会进行值拷贝,而对于自定义类型成员name,因为我们的string类实现了移动构造函数,因此它会调用string的移动构造函数进行资源的转移。
  • 而如果我们将string类当中的移动构造函数注释掉,那么Person默认生成的移动构造函数,就会调用string类中的拷贝构造函数对name成员进行深拷贝。

要验证Person类中默认生成的移动赋值函数可以用下面的代码,验证方式和上面验证移动构造的方式是一样的。

int main()
{
	Person s1("张三", 21);
	Person s2;
	s2 = std::move(s1); //想要调用Person默认生成的移动赋值

	return 0;
}

类成员变量初始化

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

class Person
{
public:
	//...
private:
	//非静态成员变量,可以在成员声明时给缺省值,用于给默认构造函数进行初始化
	string _name = "张三";
	int _age = 20;
	static int _n; //静态成员变量不能给缺省值
};

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

C++11可以让我们更好地控制要使用的默认成员函数,假设在某些情况下我们需要使用某个默认成员函数,但是因为某些原因导致无法生成这个默认成员函数,这时可以使用default关键字强制生成某个默认成员函数。下面我们来看一个场景。

class Person
{
public:
	//拷贝构造函数
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}
private:
	string _name; //姓名
	int _age;         //年龄
};

这时就会报错,因为Person类中已经有了拷贝构造函数,导致无法生成默认构造函数,因为默认构造函数生成的条件是没有编写任意类型的构造函数,包括拷贝构造函数。

int main()
{
	Person s; //没有合适的默认构造函数可用

	return 0;
}

这时我们就可以使用default关键字强制生成默认的构造函数。

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

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

 默认成员函数都可以用default关键字强制生成,包括移动构造和移动赋值。

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

当我们想限制默认函数生成,可以通过如下两种方式:

  1. 在C++98中,可以将该函数设置成私有,并且只用声明不用定义,这样当外部调用该函数时就会报错。
  2. 在C++11中,可以在该函数声明后面加上=delete,表示让编译器不生成该函数的默认版本,我们将=delete修饰的函数称为删除函数。


例如,要让一个类不能被拷贝,可以用=delete修饰将该类的拷贝构造和赋值重载。

class CopyBan
{
public:
	CopyBan()
	{}
private:
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
};

注意:被=delete修饰的函数可以设置为公有,也可以设置为私有,效果都一样。

继承和多态中final与override关键字

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

class NonInherit final //被final修饰,该类不能再被继承
{
	//...
};

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

//父类
class Person
{
public:
	virtual void Print() final //被final修饰,该虚函数不能再被重写
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Print() //重写,编译报错
	{
		cout << "hello Student" << endl;
	}
};

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

//父类
class Person
{
public:
	virtual void Print()
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Print() override //检查子类是否重写了父类的某个虚函数
	{
		cout << "hello Student" << endl;
	}
};

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

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

相关文章

[MSSQL]理解SQL Server AlwaysOn AG的备份

AG提供了以下几种备份策略 下面来看看各项的解释 Prefer Secondary(首选辅助副本) 应在辅助副本上执行此可用性组的自动备份。如果没有可用的辅助副本,将在主副本上执行备份。 这个选项只是概念上的选项。基本上,用户可以从任何复制节点上执行备份命令。 我们可以在主副本…

《计时器》是谁演唱的?

是李亚云演唱的。李亚云&#xff0c;湖南省中峰富盛控股集团有限责任公司旗下全签艺人 &#xff0c;就读于南昌航空大学本科表演专业&#xff0c;&#xff0c;一个拥有“模特、练习生、爱豆和演员”多重身份的艺人&#xff0c;2003年3月10日出生于湖南长沙&#xff0c;7岁童星出…

设计模式深度解析:AI如何影响装饰器模式与组合模式的选择与应用

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 AI如何影响装饰器模式与组合模式的选择与应用 在今天这个快速发展的技术时代&#…

C++11新特性(二):更好用的 lambda 表达式和 function 包装器

目录 lambda 表达式 基本格式及参数列表 对于 lambda 捕捉列表的说明 function 包装器 bind 包装器 lambda 表达式 C11引入了lambda表达式&#xff0c;它是一种用于创建匿名函数的语法。lambda表达式可以被视为一个匿名函数对象&#xff0c;它可以在需要函数对象的地方使用…

并查集----格子游戏

并查集中最重要的是要搞懂&#xff1a; 不明白的可以拿纸自己先演示一番&#xff0c;find函数不仅能找到他们的祖先数&#xff0c;而且同时也能更新路径的子结点都等于祖先&#xff0c;然后以后寻找时会更加的方便&#xff01;

【Linux】详解软硬链接

一、软硬链接的建立方法 1.1软链接的建立 假设在当前目录下有一个test.txt文件&#xff0c;要对其建立软链接&#xff0c;做法如下&#xff1a; ln就是link的意思&#xff0c;-s表示软链接&#xff0c;test.txt要建立软链接的文件名&#xff0c;后面跟上要建立的软链接文件名…

C语言-文件操作

&#x1f308;很高兴可以来阅读我的博客&#xff01;&#x1f31f;我热衷于分享&#x1f58a;学习经验&#xff0c;&#x1f3eb;多彩生活&#xff0c;精彩足球赛事⚽&#x1f517;我的CSDN&#xff1a; Kevin ’ s blog&#x1f4c2;专栏收录&#xff1a;C预言 1. 文件的作用 …

TransmittableThreadLocal 问题杂记

0、前言 TransmittableThreadLocal&#xff0c;简称 TTL&#xff0c;是阿里巴巴开源的一个Java库&#xff0c;它能够实现ThreadLocal在多线程间的值传递&#xff0c;适用于使用线程池、异步调用等需要线程切换的场景&#xff0c;解决了ThreadLocal在使用父子线程、线程池时不能…

Inter开发板实验汇总

背景 产品型号&#xff1a;AIxBoard-N5105 开发公司&#xff1a;蓝蛙智能 蓝蛙智能成立于2018年&#xff0c;2021年成为英特尔OpenVINO官方技术伙伴&#xff0c;2023年推出英特尔数字化开发套件爱克斯板AIxBoard-N5105。 资源 类型 网站 备注 产品介绍 ​​产品介绍 - …

华为云免费云服务器-低价云虚拟主机VPS-个人免费云服务器

华为云80款云服务产品0元试用活动&#xff0c;免费试用云服务器&#xff0c;云数据库、云速建站、云安全、CDN、OBS、Redis等云计算产品。为用户提供免费的云服务试用机会&#xff0c;帮助企业和个人轻松享受云服务。 原文&#xff1a;https://www.vpspick.com/vps/442.html …

js垃圾回收新生代和老生代以及堆栈内存详细

js 堆栈内存、新生代和老生代、垃圾回收详聊 要想了解JS内存管理就必须明白存这些js数据的内存又分为&#xff1a;栈内存和堆内存 一、 栈|堆内存(Stack|Heap) 栈(Stack)内存 原始值&#xff1a;Number、String、Boolean、Null、Undefined、Symbol和BigInt 栈内存主要存储原始…

UE5启用SteamOSS流程

一、安装OnlineSubsystemSteam插件 1、在UE里安装OnlineSubsystemSteam 2、设置默认开始地图 3、设置DefaultEngine.ini文件&#xff1a; 打开项目根目录/Config/DefaultEngine.ini文件 打开官网的配置说明 复制并粘贴到该文件中 4、设置运行模式 5、测试 确保Steam平台已…

玩转ChatGPT:Suno制作音乐

AI开始进军音乐领域了。 一款音乐AI神器——Suno V3发布&#xff0c;它能够处理从间奏到主歌、副歌、桥段直至尾奏的完整结构&#xff0c;零门槛创作音乐。 需要科学上网&#xff0c;官方网站&#xff1a;https://app.suno.ai/ 使用GPT写个歌词&#xff0c;然后丢进Suno生成…

EMD关于信号的重建,心率提取

关于EMD的俩个假设&#xff1a; IMF 有两个假设条件&#xff1a; 在整个数据段内&#xff0c;极值点的个数和过零点的个数必须相等或相差最多不能超过一 个&#xff1b;在任意时刻&#xff0c;由局部极大值点形成的上包络线和由局部极小值点形成的下包络线 的平均值为零&#x…

聚观早报 | 小米SU7正式发布;xAI推出Grok-1.5

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月30日消息 小米SU7正式发布 xAI推出Grok-1.5 红魔9 Pro新品亮相 长城汽车2023年营收 快狗打车2023年度业绩 小…

C++心决之命名空间、重载函数和引用

目录 1. C关键字(C98) 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C输入&输出 4. 缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 5. 函数重载 5.1 函数重载概念 5.2 C支持函数重载的原理--名字修饰(name Mangling) 6. 引用 6.1 引用概念 6.2 引用特性…

二维码门楼牌管理应用平台:构建社区信息新生态

文章目录 前言一、二维码门楼牌管理应用平台的功能优势二、信息展示与居民互动三、提升社区治理效能四、展望未来的发展方向 前言 随着信息技术的迅猛发展&#xff0c;二维码门楼牌管理应用平台逐渐崭露头角&#xff0c;成为社区管理的新宠。通过这一平台&#xff0c;居民可以…

Spring Boot 使用 Redis

1&#xff0c;Spring 是如何集成Redis的&#xff1f; 首先我们要使用jar包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><gro…

【docker】nexus 本地 maven配置

1、这篇文章中说明了如何搭建私服 【docker】搭建Nexus私服-CSDN博客文章浏览阅读2次。4、点击登陆&#xff08;账号&#xff1a;admin 秘密&#xff1a;在容器内 /nexus-data/admin.password 文件中)注意我的端口号是 10002&#xff0c;注意你的端口号。7、设置maven-central…

博客页面---前端

目录 主页 HTML CSS 文章详细页面 HTML CSS 登录页面 HTML CSS 文章编辑页 HTML CSS 这只是前端的页面组成&#xff0c;还没有接入后端&#xff0c;并不是完全体 主页 HTML <!DOCTYPE html> <!-- <html lang"en"> --> <head>&…