【C++】C++11-类的新功能&default&delete&final&override关键字

这篇具有很好参考价值的文章主要介绍了【C++】C++11-类的新功能&default&delete&final&override关键字。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

类的新功能

一个类有多少个默认成员函数? c++98:6个 C++11:8个

在C++98中,一个类中有如下六个默认成员函数:构造函数,析构函数,拷贝构造函数,拷贝赋值函数,取地址重载函数,const取地址重载函数,其中前四个成员函数最重要.后面两个成员函数一般不会用到

  • 其中这里“默认”的意思就是你不写编译器会自动生成.

新增的默认成员函数

C++11标准中又增加了两个默认成员函数: 移动构造函数和移动赋值重载函数

默认移动构造和移动赋值函数的生成条件

  • 移动构造函数的生成条件:如果我们没有自己实现移动构造函数, 并且没有自己实现析构函数、拷贝构造函数和拷贝赋值函数
  • 移动赋值重载函数的生成条件:没有自己实现移动赋值重载函数.并且没有自己实现析构函数、拷贝构造函数和拷贝赋值函数

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

需要注意的是: 如果我们自己实现了移动构造或者移动赋值.就算没有实现拷贝构造和拷贝赋值.编译器也不会生成默认的拷贝构造和拷贝赋值


默认生成的移动构造和移动赋值所做的工作

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

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


验证

我们这里模拟实现一个简化版的string,类当中只编写了几个我们需要用到的成员函数.

namespace Mango
{
	class string
	{
	public:
		//构造函数
		string(const char* str = "")
		{
			_size = strlen(str); //初始时.字符串大小设置为字符串长度
			_capacity = _size; //初始时.字符串容量设置为字符串长度
			_str = new char[_capacity + 1]; //为存储字符串开辟空间(多开一个用于存放'\0')
			strcpy(_str, str); //将C字符串拷贝到已开好的空间
		}
		//交换两个对象的数据
		void swap(string& s)
		{
			::swap(_str, s._str); //交换两个对象的C字符串
			::swap(_size, s._size); //交换两个对象的大小
			::swap(_capacity, s._capacity); //交换两个对象的容量
		}
		//拷贝构造函数(现代写法)
		string(const string& s)
			:_str(nullptr), _size(0), _capacity(0)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str); //调用构造函数.构造出一个C字符串为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); //用s拷贝构造出对象tmp
			swap(tmp); //交换这两个对象
			return *this; //返回左值(支持连续赋值)
		}
		//移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}
		//析构函数
		~string()
		{
			delete[] _str;  //释放_str指向的空间
			_str = nullptr; //及时置空.防止非法访问
			_size = 0;      //大小置0
			_capacity = 0;  //容量置0
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

string类的拷贝构造、拷贝赋值、移动构造和移动赋值函数中都打印了一条提示语句,可以通过控制台输出判断是否调用了对应的函数


然后再编写一个简单的Person类.Person类中的成员name的类型就是我们模拟实现的string类

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:
	Mango::string _name; //姓名
	int _age;         //年龄
};

虽然Person类当中没有实现移动构造和移动赋值,但拷贝构造,拷贝赋值和析构函数都实现了,因此Person类中不会生成默认的移动构造和移动赋值

验证移动构造: 下述代码中,用一个右值去构造s2对象,

int main()
{
	Person s1("张三", 21);
	Person s2 = std::move(s1); //想要调用Person默认生成的移动构造
    //实际上调用的是:string(const string& s) -- 深拷贝
	return 0;
}

由于Person类没有生成默认的移动构造函数,因此这里会调用Person的拷贝构造函数,由于拷贝构造函数的参数是const Person& p 既能接收左值也能接收右值,所以不会有问题, 这时在Person的拷贝构造函数中就会调用string的拷贝构造函数对name成员进行深拷贝, 所以这里即使move了,因为没有资源转移,所以s1不会变为空


如何让Person生成默认的移动构造函数:

将Person类中的拷贝构造、拷贝赋值和析构函数全部注释掉!这时用右值去构造s2对象时就会调用Person默认生成的移动构造函数,对于这个移动构造函数来说:

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

验证移动赋值:验证方式和上面验证移动构造的方式是一样的

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

类成员变量初始化

对于默认生成的构造函数:自定义类型的成员会调用其构造函数进行初始化,但并不会对内置类型的成员进行处理

C++11支持非静态成员变量在声明时进行初始化赋值,默认生成的构造函数会使用这些缺省值对成员进行初始化

例子:

class Person
{
public:
	//...
private:
	//非静态成员变量.可以在成员声明时给缺省值
	Mango::string _name = "张三"; //姓名
	int _age = 20;             //年龄
	static int _n; //静态成员变量不能给缺省值
};

注意:这里不是初始化.而是给声明的成员变量一个缺省值,真正的初始化是在初始化列表当中!


default关键字

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


例子1:下面的Person类中实现了拷贝构造函数

class Person
{
public:
	//拷贝构造函数
	Person(const Person& p)
		:_name(p._name), _age(p._age)
	{}
private:
	Mango::string _name; //姓名
	int _age;         //年龄
};
int main()
{
	Person s; //报错:没有合适的默认构造函数可用
	return 0;
}

这时上述代码就无法编译成功了,我们需要回归下构造的定义:

  • 如果你写了构造,就不会生成默认构造,虽然我们只写了拷贝构造,但是拷贝构造是特殊的构造函数

因为Person类中编写了拷贝构造函数,导致无法生成默认的构造函数,因为默认构造函数生成的条件是没有编写任意类型的构造函数,包括拷贝构造函数-它是特殊的构造函数,

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

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

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

说明一下: 默认成员函数都可以用default关键字强制生成.包括移动构造和移动赋值.


delete关键字

当我们想要限制某些默认函数生成时.可以通过如下两种方式

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

例子:要让一个类不能被拷贝

C++98写法:

err版本

class CopyBan
{
public:
	CopyBan()
	{}
    void f()
    {
        CopyBan a = *this;
    }
private:
	CopyBan(const CopyBan&)
    {}
};

此时虽然可以防止别人在类外进行拷贝,但是在类里面还是可以进行拷贝


正确做法:C++98 防拷贝:1、只声明不实现 2、声明成私有

问:能否只有条件1?

不能!因为别人可以在类外实现了,也可以用!加上条件2之后,就算你在类外实现了,由于是私有的,类外面也用不了,但是对于类里面的,照样可以拷贝!


class CopyBan
{
public:
	CopyBan()
	{}
    void f()
    {
        CopyBan a = *this;//不报错
    }
private:
	CopyBan(const CopyBan&);//C++98 防拷贝:1、只声明不实现 2、声明成私有
};

C++11写法:

class CopyBan
{
public:
	CopyBan()
	{}
    void f()
    {
        CopyBan a = *this;//此时就会报错
    }
private:
	CopyBan(const CopyBan&) = delete;
};

说明一下: 被=delete修饰的函数可以设置为公有.也可以设置为私有.效果都一样.


final与override关键字

final与override关键字一般都是用在继承和多态当中:

关于final

final修饰类:被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() //如果子类重写了Print函数,编译报错
	{
		cout << "hello Student" << endl;
	}
};

关于override关键字:

override修饰虚函数: override修饰子类的虚函数,检查子类是否重写了父类的某个虚函数,如果没有没有重写则编译报错文章来源地址https://www.toymoban.com/news/detail-432050.html

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

到了这里,关于【C++】C++11-类的新功能&default&delete&final&override关键字的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 官宣Windows11十月的Moment 1来了,新功能!附官方ISO镜像(简/繁/英)

    一个月前(2022年9月)微软正式发布了Windows 11的年度大更新——22H2版本(Build 22621.382),过了没几天当月又推出了Build 22621.525的ISO镜像。给人一种特别操之过急的感觉~ 前天微软又向MVS订阅用户推送了10月份的累积更新的ISO镜像,系统版本号Build 22621.674。Windows11也是维持着每

    2024年02月06日
    浏览(54)
  • Java 20 新功能介绍

    Java 20 共带来 7 个新特性功能 ,其中三个是孵化提案,孵化也就是说尚在征求意见阶段,未来可能会删除此功能。 JEP 描述 分类 429 作用域值(孵化器) Project Loom,Java 开发相关 432 Record 模式匹配(第二次预览) Project Amber,新的语言特性 433 switch 的模式匹配(第四次预览)

    2024年02月03日
    浏览(64)
  • Midjourney新功能:角色参照指南

    基本概念 角色参照(Character Reference) :这个功能允许用户在不同的图像生成中保持给定参照角色的一致性。 适用模型 :适用于Midjourney V6和Niji6型号。 功能亮点 跨风格一致性 :可以在不同风格(如动漫风、写实风)中保持角色特征一致。 面部、着装、发型调控 :用户可以

    2024年04月10日
    浏览(60)
  • TypeScript 5.1发布,新功能更新

    1:返回类型增加undefined 这里设置了一个别名 fun,当时使用它的时候,我们必须显示返回一个 undefined 。 现在你可以直接设置返回类型: 而不仅限于 void any 。 4.3版本 :❌ 5.1版本 :✅ 2:getter可以设置和 setter 的不相关类型 在之前版本 ,get 返回类型应该为 set 的子类型,如

    2024年02月09日
    浏览(50)
  • SOLIDWORKS 2023新功能揭秘(一):3D CAD功能的十大更新

    SolidWorks 3D CAD  软件拥有设计、模拟、成本估算、可制造性检查、CAM、可持续设计和数据管理等功能,同时还包含适用于钣金,焊件,曲面,模具,产品配置,DFM和CAM的专业工具,支持ECAD/MCAD协作,复杂的零部件库以及高级真实感渲染。更重要的是具有结构和运动分析功能,

    2024年02月05日
    浏览(48)
  • 三星泄露微软 Copilot 新功能:用自然语言操控各种功能

    3 月 11 日消息,微软计划本月晚些时候发布新款 Surface 电脑和适用于 Windows 11 的 Copilot 新功能,但三星似乎等不及了,在其即将推出的 Galaxy Book4 系列产品宣传材料中泄露了一些即将到来的 Copilot 功能。 三星官网上发布的图片证实了此前关于微软正为其人工智能助手 Copilo

    2024年04月09日
    浏览(87)
  • C# 12 预览版的新功能

    作者:Kathleen Dollard 排版:Alan Wang Visual Studio 17.7 Preview 3 和 .NET 8 Preview 6 的发布推进了 C# 12的发展。此预览版包含的功能为将来的性能增强奠定了基础。现在,您能够在库中更方便的使用内联函数。此预览版首次推出了一项实验性功能:拦截器。该功能允许生成器重新路由代

    2024年02月14日
    浏览(44)
  • 揭密.NET 8到底有什么新功能

    .NET 8 是微软于2021年8月24日宣布的下一代编程语言和框架,它是 .NET 宇宙的一部分,与 C# (Common Language Infrastructure) 紧密集成。.NET 8 引入了许多新功能,如原生编译、值类型 (Value Types)、结构化并发 (structured concurrency) 和快速数组 (RapidArray)。.NET 8 还支持本机 (native) AOT (Ahead-Of

    2024年02月03日
    浏览(45)
  • Microsoft Releases .NET 7新功能

    Microsoft Visual Studio是一种统一的开发体验,使开发人员能够跨web、云和设备创建多层应用程序。11月8日,微软发布了该强大开发环境的下一版本:Visual Studio 2022 17.4版。 除了修复许多顶级报告的bug之外,17.4版还包括了许多基于开发者社区建议的新功能,包括: Visual Studio的本

    2024年02月06日
    浏览(47)
  • 探索PostgreSQL的新功能:最新版本更新解析

    PostgreSQL作为一种强大而开源的关系型数据库管理系统,不断在不断进化和改进。每一次的版本更新都带来了更多功能和改进,让用户在处理大规模数据和复杂查询时体验更好的性能和功能。在本文中,我们将深入探索PostgreSQL的最新版本更新,了解新增的功能和改进,以及这

    2024年02月14日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包