类的新功能

news2025/1/9 16:31:10

类的新功能

默认成员函数

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

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

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

相关文章

Js之运算符与表达式

运算符&#xff1a;也叫操作符&#xff0c;是一种符号。通过运算符可以对一个或多个值进行运算&#xff0c;并获取运算结果。 表达式&#xff1a;由数字、运算符、变量的组合&#xff08;组成的式子&#xff09;。 表达式最终都会有一个运算结果&#xff0c;我们将这个结果称…

从输入url到页面展示的过程

唠唠叨&#xff1a;我不想误人子弟&#xff0c;我这篇算是搬运工&#xff0c;加上自己的理解做点总结&#xff0c;所以还请大家科学上网去看这篇&#xff1a;https://aws.amazon.com/cn/blogs/mobile/what-happens-when-you-type-a-url-into-your-browser/ 是这六个步骤&#…

springboot论坛管理系统

论坛管理系统 摘要&#xff1a; 在社会快速发展的影响下&#xff0c;论坛管理系统继续发展&#xff0c;使论坛管理系统的管理和运营比过去十年更加信息化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上论坛管理系统是一项十分重要并且有价值的事情。对于传统的论…

如何提升公众号搜索量?分享内部运营的5步优化技术!

最近一直有自媒体同行朋友在写关于公众号的内容&#xff0c;很多都说公众号现在没得玩了。其实&#xff0c;在运营自媒体上面&#xff0c;思维不通&#xff0c;技术不到位&#xff0c;哪个平台都不适合你玩。 想要在自媒体上面运营变现&#xff0c;一定不要先点击广告变现&…

【Linux】详解文件系统以及周边知识

一、磁盘的基本知识 磁盘中可以被划分成一个一个的环&#xff0c;每个环都是一个磁道。每个磁道又可以被均分成一个一个的扇区&#xff0c;扇区是磁盘IO的基本单位&#xff08;想要修改扇区中的一个比特位就必须把该扇区的全部比特位都加载到内存中&#xff09;。磁盘中的盘面&…

「MySQL」索引事务

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;数据库 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 索引&事务 &#x1f349;索引&#x1f34c;特点&#x1f34c;通过 SQL 操作索引&#x1f34c;底层数据结构 &#x1f349;事务&…

张颂文|永远保持好奇心的人,是永远进步的人。

哈喽,你好啊,我是雷工! 今天看到了张颂文的一段演讲,提到了他因为好奇心而被改变的人生。 如果想把单一和枯燥的工作做的更好,张颂文的办法是像一个孩子一样保持好奇心,不停地提出一些有趣的问题。 在5年的导游经历中,对每次游览的地点都像初次游览般保持好奇心,正因为…

C++从入门到精通——引用()

C的引用 前言一、C引用概念二、引用特性交换指针引用 三、常引用保证值不变权限的方法权限的放大权限的缩小权限的平移类型转换临时变量 四、引用的使用场景1. 做参数2. 做返回值 五、传值、传引用效率比较值和引用的作为返回值类型的性能比较 六、引用和指针的区别引用和指针的…

工单系统的作用与优势!为什么企业需要它?

什么是工单系统&#xff1f;工单系统作为企业服务类工具&#xff0c;能在管理上和业务上为企业带来什么帮助吗? 什么是工单系统 ZohoDesk工单系统是一种用于管理和处理任务或请求的软件工具。它提供了一个集中的平台&#xff0c;使组织能够跟踪、分配和解决各种问题、请求和…

AWS创建IAM用户,以及通过IAM用户登录

基本概念&#xff1a; IAM Identity Center&#xff08;AWS SSO&#xff09; 跨账户访问&#xff1a;IAM Identity Center允许用户使用他们自己的单一登录凭证来访问多个AWS账户和应用程序。这意味着你可以拥有一个账户和密码&#xff0c;通过IAM Identity Center的用户门户&…

前端虚拟滚动列表 vue虚拟列表

前端虚拟滚动列表 在大型的企业级项目中经常要渲染大量的数据&#xff0c;这种长列表是一个很普遍的场景&#xff0c;当列表内容越来越多就会导致页面滑动卡顿、白屏、数据渲染较慢的问题&#xff1b;大数据量列表性能优化&#xff0c;减少真实dom的渲染 看图&#xff1a;绿色…

攻防世界——catfly

这道题我觉得很难&#xff0c;我当初刷题看见这道题&#xff0c;是唯一一道直接跳过的&#xff0c;现在掌握了一点知识才回来重新看 这道题在linux运行下是这样&#xff0c;我首先猜测是和下面这个time有关&#xff0c;判断达到一定次数就会给我flag 但是我找了好久都没找到那…

NFT-前端开发(一)

使用 在我们想要保存项目的目录下打开终端运行npx create-react-app test2命令初始化&#xff0c;test2是我们的项目名字&#xff0c;可以自己去更改。 初始化完成后&#xff0c;我们目录下就会多出一个test2文件夹 &#xff0c;然后我们在vscode中打开该文件夹 然后我们打开j…

C++教学——从入门到精通 5.单精度实数float

众所周知&#xff0c;三角形的面积公式是(底*高)/2 那就来做个三角形面积计算器吧 到吗如下 #include"bits/stdc.h" using namespace std; int main(){int a,b;cin>>a>>b;cout<<(a*b)/2; } 这不对呀&#xff0c;明明是7.5而他却是7&#xff0c;…

(一)kafka实战——kafka源码编译启动

前言 本节内容是关于kafka消息中间键的源码编译&#xff0c;并通过idea工具实现kafka服务器的启动&#xff0c;使用的kafka源码版本是3.6.1&#xff0c;由于kafka源码是通过gradle编译的&#xff0c;以及服务器是通过scala语言实现&#xff0c;我们要预先安装好gradle编译工具…

暴力破解笔记

1 暴力破解简介 暴力破解&#xff1a; 蛮力攻击&#xff0c;又称为穷举攻击&#xff0c;或暴力破解&#xff0c;将密码进行逐个尝试验证&#xff0c;直到尝试出真正的密码为止。 暴力破解是指采用反复试错的方法并希望最终猜对&#xff0c;以尝试破解密码或用户名或找到隐藏的…

进无止境,砥砺前行,互联网营销专家杨建允助力多个品牌和机构提升营运效率!

进无止境&#xff0c;砥砺前行&#xff01;在过去的一年&#xff08;2023年&#xff09;&#xff0c;互联网营销专家杨建允持续为数十家品牌和机构提供了品牌和营销支持&#xff01;统计如下&#xff1a;&#xff08;排名不分先后&#xff09; 滴滴&#xff1b; 企查查&#…

了解XSS和CSRF攻击与防御

什么是XSS攻击 XSS&#xff08;Cross-Site Scripting&#xff0c;跨站脚本攻击&#xff09;是一种常见的网络安全漏洞&#xff0c;它允许攻击者在受害者的浏览器上执行恶意脚本。这种攻击通常发生在 web 应用程序中&#xff0c;攻击者通过注入恶意脚本来利用用户对网站的信任&…

属性选择器

1.[title]{background:yellow;}&#xff1a;所有带title标签设置成黄色 2.div[class]{background:yellow;}&#xff1a;所有div中带class标签设置成黄色 3.div[classbox1]{border:1px solid blue; }&#xff1a;div中包含class并且classbox1的设置成蓝边框 4. class…

【Linux】进程实践项目 —— 自主shell编写

送给大家一句话&#xff1a; 不管前方的路有多苦&#xff0c;只要走的方向正确&#xff0c;不管多么崎岖不平&#xff0c;都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》 自主shell命令编写 1 前言2 项目实现2.1 创建命令行2.2 获取命令2.3 分割命令2.4 运行命令 3 源代码…