【C++11】final与override关键字和类的新功能

news2025/1/10 2:10:44

目录

一、final与override关键字

1.1 final 

1.2 override

二、类的新功能

2.1 默认成员函数

2.2 类成员变量初始化

2.3 default关键字

2.4 delete关键字


注意:C++专栏的所有测试代码都是在 vs2019 的环境下编译运行的 

一、final与override关键字

这两个关键字用于继承和多态

1.1 final 

final:修饰虚函数,表示该虚函数不能再被重写

测试代码

//基类
class Person {
public:
	//被final修饰,该虚函数不能再被重写
	virtual void BuyTicket() final
	{ 
		cout << "Person-买票-全价" << endl; 
	}
};
//派生类
class Student : public Person {
public:
	//派生类的虚函数重写了父类的虚函数
	virtual void BuyTicket() { cout << "Student-买票-半价" << endl; }
};

编译直接报错

final 关键字也可用于实现一个不能被继承的类  

如何实现一个不能被继承的类??

  1. 把构造函数进行私有,这是 C++98 的做法
  2.  类定义时 加 final 关键字,这是 C++11的做法

 测试代码

//基类使用 final 修饰
class A final
{};
//派生类无法进行继承基类
class B : public A
{};

编译直接报错

1.2 override

override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

测试代码

//基类
class Person {
public:
	//基类的虚函数
	virtual void BuyTicket() { cout << "Person-买票-全价" << endl; }
};
//派生类
class Student : public Person {
public:
	//派生类完成了虚函数的重写,编译通过
	virtual void BuyTicket()override
	{ 
		cout << "Student-买票-半价" << endl; 
	}
};
//派生类
class Soldier : public Person
{
public:
	//派生类没有完成虚函数的重写,编译报错
	virtual void BuyTicket(int n)override
	{ 
		cout << "Soldier-优先-买票" << endl;
	}
};

 编译直接报错

二、类的新功能

2.1 默认成员函数

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

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

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的

C++11 新增了两个:移动构造函数和移动赋值运算符重载

默认移动构造的生成条件

如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。

移动构造的默认行为: 

  • 默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造 

默认移动赋值的生成条件

如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。

移动赋值的默认行为:  

默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)

注意如果提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

测试代码,需要使用简化版模拟实现的string

namespace fy
{
	class string
	{
	public:
		//构造函数
		string(const char* str = "")
		{
			_size = strlen(str);//字符串大小
			_capacity = _size;//构造时,容量大小默认与字符串大小相同
			_str = new char[_capacity + 1];//为字符串开辟空间(多开一个用于存放'\0')
			strcpy(_str, str);//将C字符串拷贝到已开好的空间
		}	
		//拷贝构造 -- 现代写法
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			,_capacity(0)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);//复用构造函数,构造 tmp对象
			swap(tmp);//交换
		}
		//赋值重载 -- 现代写法1
		string& operator=(const string& s)
		{
			cout << "string& operator=(const string& s) -- 深拷贝" << endl;
			if (this == &s)//检查自我赋值
			{
				return *this;
			}
			string tmp(s);//复用拷贝构造函数,用s拷贝构造出对象tmp
			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指向的空间
			_str = nullptr;
			_size = _capacity = 0;
		}
		//交换两个字符串
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

 然后再编写一个简单的 Person类,该类使用了我们模拟实现的string

class Person
{
public:
	//构造函数
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
private:
	fy::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	Person s4;
	s4 = std::move(s2);
	return 0;
}

注意:这里的 Person类没有实现拷贝构造、赋值重载、析构函数,只实现了构造函数,即满足移动赋值和移动拷贝函数的生成条件

运行结果如下

 默认生成的移动构造和移动赋值函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值,这里完全符合

下面进行测试

一但 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:
	fy::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	Person s4;
	s4 = std::move(s2);
	return 0;
}

运行结果

由于默认的移动赋值和移动拷贝函数没有生成,此时 Person类只能调用 string的深拷贝函数,调不到移动赋值和移动构造函数

2.2 类成员变量初始化

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化,这里在类和对象的篇章已经谈过

class Person
{
public:
	//...
private:
	//非静态成员变量,可以在成员声明时给缺省值
	string _name = "张三"; //缺省值
	int _age = 22;         //缺省值

	static int _n; //静态成员变量不能给缺省值
};

注意:这里不是初始化,给的的值是缺省值

2.3 default关键字

default关键字的作用是:强制生成默认函数

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

使用:在需要强制生成默认函数的声明后面加上 =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)
	{}
	//移动构造
	Person(Person&& p) = default;
private:
	fy::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

运行结果,使用了 default关键字后默认的移动构造可以生成,即便实现了拷贝构造函数

2.4 delete关键字

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

使用:在需要禁止生成默认函数的声明后面加上 =delete 即可 

如果能想要限制某些默认函数的生成:

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

测试代码:

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

class A
{
public:
	A()
	{}
private:
	//强制不允许生成
	A(const A&) = delete;
	A& operator=(const A&) = delete;
};

int main()
{
	A a1;
	A a2(a1);
	return 0;
}

编译报错

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新

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

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

相关文章

C++基础入门

C基础入门1.C认识1.1 第一个C程序1.1.1 创建项目1.1.2 创建文件1.1.3 编写代码1.1.4 运行程序1.3 变量1.4 常量1.5 关键字1.6 标识符命名规则2 数据类型2.1 整型2.2 sizeof关键字2.3 实型&#xff08;浮点型&#xff09;2.4 字符型2.5 转义字符2.6 字符串型2.7 布尔类型 bool2.…

Session、Cookie和Token

Session、Cookie和Token 参考&#xff1a;Session、Cookie、Token 【浅谈三者之间的那点事】 HTTP协议是一种无状态协议&#xff0c;即每次服务端接收到客户端请求时&#xff0c;都是一个全新的请求&#xff0c;服务器并不知道客户端的历史请求记录&#xff1b;Session和Cooki…

统信UOS + Windows双系统安装教程

全文导读&#xff1a;本文主要介绍了AMD架构下&#xff08;Intel/amd/兆芯/海光&#xff09;的机器同时安装Windows系统UOS系统的方法。 准备环境 1、下载好UOS系统镜像&#xff08;AMD64&#xff09;&#xff0c;下载地址&#xff1a;https://www.chinauos.com/resource/down…

第6章 接口、lambda表达式与内部类

文章目录Java与C异Java与C同接口接口概念接口与抽象类静态和私有方法默认方法解决默认方法冲突接口与回调&#xff08;不明白&#xff09;Comparator接口为什么有比较器Comparator接口实现步骤示例问题对象克隆为什么克隆浅拷贝&#xff1a;Object.clone方法的问题深拷贝数组的…

Firefox浏览器-渗透测试插件推荐

在日常工作中可能需要一些浏览器插件辅助我们做工作 下面是比较好的&#xff0c;当然不一定对你有用&#xff0c;找到适合自己的即可 FoxyProxy FoxyProxy是一个高级的代理管理工具&#xff0c;它完全替代了Firefox有限的代理功能。它提供比SwitchProxy、ProxyButton、 Quick…

3-5年以上的功能测试如何进阶自动化?【附学习路线】

做为功能测试人员来讲&#xff0c;从发展方向上可分两个方面&#xff1a; 1、业务流程方向 2、专业技能方向。 当确定好方向后&#xff0c;接下来就是如何达到了。(文末自动化测试学习资料分享) 一、业务流程方向 1、熟悉底层的业务 作为功能测试工程师来讲&#xff0c;了解…

Arduino-环境搭建

Arduino-环境搭建 文章目录Arduino-环境搭建下载开发软件调研Arduino入门教程本次学习所使用的Arduino开发板型号下载开发板的包——电脑联网&#xff0c;先插上开发板&#xff0c;再打开Arduino IDEArduino串口接收实验Arduino蓝牙接收实验Arduino蓝牙发送实验下载开发软件 官…

【python-Unet】计算机视觉~舌象舌头图片分割~机器学习(三)

返回至系列文章导航博客 1 简介 舌体分割是舌诊检测的基础&#xff0c;唯有做到准确分割舌体才能保证后续训练以及预测的准确性。此部分真正的任务是在用户上传的图像中准确寻找到属于舌头的像素点。舌体分割属于生物医学图像分割领域。分割效果如下&#xff1a; 2 数据集介…

一些二叉树相关面试题

文章目录1. 对折2. 判断是否是平衡二叉树3. 判断是否是搜索二叉树4. 二叉树的直径5. 寻找最大二叉搜索树6. 用递归套路判断是否是完全二叉树7. 派对的最大快乐值1. 对折 这个大家可以自己用纸对折一下&#xff0c;我这里就简单的说一下&#xff1a; 这是我们第一次对折的情况。…

使用PHP做个图片防盗链(全网详解)

概念&#xff1a; 防盗链是一种防范网络图片、视频等资源被他人盗链&#xff08;直接在其它网站使用&#xff09;的技术。在网站上添加防盗链功能可以防止其他网站恶意盗取自己网站的图片等内容&#xff0c;减少带宽消耗和保护网站内容安全。通常实现防盗链的方式是在网站服务…

js 使用 Array.from 快速生成0~5,步进值为0.1的数组

一、我们平常用的比较多的方法是for循环生成 let data[] for(let i0;i < 51;i){data.push(i/10) }二、用Array.from生成 先来认识一下我们今天的主角&#xff01;&#xff01;&#xff01; 1、释义 Array.from() 方法从一个类似数组或可迭代对象创建一个新的&#xff0c…

SpringMVC(三):请求流程处理

一、引言&#xff1a; 如下是我画的一个简单的SpringMVC的请求流程图&#xff0c;接下来会通过请求流程图去进行源码分析。 [1 ] 当我们客户端发送请求时&#xff0c;Servlet会进行请求的解析&#xff0c;然后交给DispatcherServlet进行统一分发。[2] DispatcherServlet会根…

北京君正案例:超能面板PRO采用4英寸IPS超清多彩屏,值不值得买?

清晨&#xff0c;窗帘自动拉开&#xff0c;悦耳音乐缓缓响起&#xff0c;面包机、咖啡机自动工作&#xff0c;开启新一天。离家时&#xff0c;一键关掉所有灯光和家电&#xff0c;节能安全&#xff0c;手机上便可查看家里设备状态&#xff0c;不用担心门没锁、灯没关等问题。下…

ClickHouse入门详解

ClickHouse基础部分详解一、ClickHouse简介二、ClickHouse单机版安装2.1、ClickHouse安装前准备环境2.2、ClickHouse单机安装三、ClickHouse数据类型四、ClickHouse的表引擎一、ClickHouse简介 对于其他乱起八糟的简介&#xff0c;我就不写了&#xff0c;只写干货. ClickHouse总…

Lumen6 /laravel 框架路由请求实现token验证

版本 Lumen6.0 中文文档&#xff1a;https://learnku.com/docs/lumen/5.7/cache/2411 实现功能效果 1、使用缓存存储用户token 2、从请求头head 中获取用户token 3、返回指定的认证失败结构体 4、对指定的接口路由做身份验证 第一步&#xff1a;解除注释 注意&#xff1…

QML控件--Container

文章目录一、控件基本信息二、控件说明三、属性成员四、成员函数一、控件基本信息 Import Statement: import QtQuick.Controls 2.14 Since: Qt 5.7 Inherits: Control Inherited By: DialogButtonBox, MenuBar, SwipeView, and TabBar 二、控件说明 Container&#xff08;容…

网络安全之从原理看懂 XSS

01、XSS 的原理和分类 跨站脚本攻击 XSS(Cross Site Scripting)&#xff0c;为了不和层叠样式表(Cascading Style Sheets&#xff0c;CSS)的缩写混淆 故将跨站脚本攻击缩写为 XSS&#xff0c;恶意攻击者往 Web 页面里插入恶意 Script 代码&#xff0c;当用户浏览该页面时&…

【产品设计】删除确认文案,猛男落泪

使用各种系统时&#xff0c;都有各种删除操作&#xff0c;用户在删除时&#xff0c;很少关注文案写了什么&#xff0c;但这个文案往往让产品经理们殚精竭虑。怎么样才能写出合格的删除确认文案呢&#xff1f; 使用各种系统的时候&#xff0c;都有各种删除操作&#xff0c;作为用…

substrate中打印调试信息的多种方式详解

目录1. 获取substrate-node-template代码2. 添加一个用于测试的pallet至依赖到pallets目录3. log方式来输出信息3.1 将log依赖添到cargo.toml文件3.2 log-test/src/lib.rs修改call方法3.3 polkadot.js.调用测试函数do_something_log_test4. printable trait方式来输出信息4.1 首…

在 Rainbond 上使用在线知识库系统zyplayer-doc

zyplayer-doc 是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;提供在线化的知识库管理功能&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;可以完全以内网的方式来部署使用它。 当然也可以将其作为企业产品的说明文档…