C++11 类的新功能

news2024/12/22 22:45:13

作者:@小萌新
专栏:@C++进阶
作者简介:大二学生 希望能和大家一起进步!
本篇博客简介:介绍C++11类的新功能和一些关键字

类的新功能

  • 类的新功能
    • 默认成员函数
    • 类成员变量的初始化
  • C++11新关键字
    • default
    • delete
    • final
    • override

类的新功能

默认成员函数

在C++98中 类的默认成员函数一般可以认为有六个

这六个默认成员函数分别是

  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值函数
  • 取地址运算符重载
  • const取地址运算符重载

之所以称之为默认成员函数意思就是即使我们不写这六个成员函数 他们也会自己生成

在实际的写代码过程中前面四个默认成员函数是比较重要的 我们需要自己实现一遍 而后面两个让系统自己默认生成就好

在C++11中为了提高效率 提出了右值引用的概念 因此默认成员函数也增加了两个 变成了八个

增加了两个默认成员函数分别是

  • 移动构造函数
  • 移动赋值重载函数

但是他们的默认生成条件有些不同 他们两个的默认生成条件分别是

移动构造函数: 没有自己实现移动构造函数 并且没有自己实现析构函数 拷贝构造函数和拷贝赋值函数

移动赋值重载函数: 没有自己实现移动赋值重载函数 并且没有自己实现析构函数 拷贝构造函数和拷贝赋值函数

这里还有一点需要特别注意的是: 如果我们自己生成了移动构造和移动赋值重载函数 那么就算我们没有生成拷贝构造和拷贝赋值 系统也不会默认生成

也就是说这两对函数之间是有相互作用的 不能单独拆开来看

默认的移动构造和移动赋值会做什么呢

  • 默认移动构造 对于内置类型 它会完成浅拷贝 对于自定义类型 如果它实现了移动构造就会调用移动构造 否则就调用拷贝构造
  • 默认移动赋值 对于内置类型 它会完成浅拷贝 对于自定义类型 如果它实现了移动赋值就会调用移动赋值 否则就调用拷贝赋值

验证默认移动构造和移动赋值

首先我们先创造出一个简单的string类 直接复用上一篇博客的代码就好

namespace shy
{
	class string
	{
	public:
		typedef char* iterator;

		// begin迭代器返回第一个元素
		iterator begin()
		{
			return _str;
		}

		// end迭代器返回最后一个元素后一个元素的位置
		iterator end()
		{
			return _str + _size;
		}

		// 构造函数
		string(const char* str = "")
		{
			_size = strlen(str); // 初始化设置字符串大小
			_capacity = _size;
			_str = new char[_capacity + 1]; // 这里要多开一个空间来存储/0
			strcpy(_str, str);
		}

		// 交换两个对象的数据 
		void swap(string& s)
		{
			::swap(_str, s._str);
			::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); // 调用构造函数
			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& operatpr=(string&& s)" << endl;
			swap(s);
			return *this;
		}

		// 析构函数
		~string()
		{
			delete[] _str; // 释放str的空间
			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}

		//[]运算符重载
		char& operator[](size_t i)
		{
			assert(i < _size);
			return _str[i]; // 返回左值引用
		}

		// 改变容量 大小不变
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strncpy(tmp, _str, _size + 1); // 这里不使用strcpy的原因是字符串中哟i可能出现/0
				delete[] _str;
				_str = tmp;
				_capacity = n; // 容量改变
			}
		}

		// 尾插字符
		void push_back(char ch)
		{
			// 首先判断容量是否足够
			if (_size >= _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			// 尾插到最后 最后加上\0
			_str[_size] = ch;
			_str[_size + 1] = '\0';
			_size++; 
		}

		//+=运算符重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		// 返回c类型的字符串
		const char* c_str() const //这里加const是修饰this指针 让它的权限变成只读 为了防止后面的只读对象调用这个函数
		{
			return _str;
		}

	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)
	{}
	//拷贝构造函数
	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:
	shy::string _name; //姓名
	int _age;         //年龄
};

我们的person类中没有实现移动构造和移动赋值 但是实现了拷贝构造 析构和赋拷贝赋值

所以说移动构造和移动赋值并不会默认生成

那么我们下面就会有一段代码来证明上面的话

	Person p1("zhangsan",18);
	Person p2 = ::move(p1);

我们这里使用了右值去构造p2 假设p2的移动构造存在的话 那么因为string是我们的自定义类型

所以说它就回去调用string的移动构造

那么我们来看看实际上允许的结果是什么

在这里插入图片描述
实际上它调用的是string类的拷贝构造

从这个试验就可以证明并没有默认生成移动构造函数

那么如果要生成移动构造和移动赋值我们就必须将原person代码中的 析构函数 拷贝构造 拷贝赋值全部注释掉

那么注释掉之后我们再来看看效果是什么样子的

在这里插入图片描述
接下来我们用另外一段代码试验一下移动赋值

	Person p1("zhangsan",18);
	Person p2;
	p2 = ::move(p1);

在这里插入图片描述
我们可以发现 移动构造和移动赋值的生成条件符合我们之前的学习

这里还有一点需要注意的 有些比较古老的编译器可能不支持C++11 所以在这些编译器上无法进行我们上面的试验

类成员变量的初始化

C++98之前的类成员默认构造初始化规则特别奇怪 是这样子的

  • 对于自定义类型来说会调用他们的构造函数进行初始化
  • 对于内置类型不进行初始化

而在C++11中 对于内置类型打了个补丁

  • 对于自定义类型来说会调用他们的构造函数进行初始化
  • 对于内置类型我们可以使用缺省值来进行初始化

以我们的person类来说

	shy::string _name; //姓名
	int _age = 18;     //年龄

如果我们使用默认的构造函数 那么他们的年龄就会被默认初始化为18

C++11新关键字

default

default关键字的功能是强制生成默认成员函数

在某些情况下 我们可能需要用到某个默认成员函数 但是它自己并不会默认生成 这个时候就到了我们的default关键字派上用场的时候了

比如说像下面的情况

class Person
{
public:

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

private:
	shy::string _name; //姓名
	int _age = 18;      //年龄
};

在上面的代码中 我们只生成了person的拷贝构造函数

但是我们直到拷贝构造函数就是一种特殊的构造函数

所以说当我们生成了它的拷贝构造函数后 person类并不会生成默认构造函数了

但是这样子就会出现下面的情况

在这里插入图片描述
因为没有合适的默认构造函数而出错

所以这个时候我们就需要编译器生成默认构造函数 于是乎我们应该在类中加上这段代码

   Person() = default;

这样子代码就可以运行了

在这里插入图片描述

delete

delete的作用是可以禁止生成默认成员函数

在C++98中我们如果构造一个不能被拷贝的类我们只能这样子做

  • 将拷贝构造函数私有
  • 将拷贝构造函数只声明不定义

这二者缺一不可

如果我们只将构造函数私有那么通过另外的成员函数完全可以在类里面拷贝再返回出被拷贝的类

如果我们只声明不定义那么完全可以再类外面定义再使用

而在C++11中提供了delete关键字

我们如果不想要一个类被拷贝只需要delete修饰将该类的拷贝构造和拷贝赋值

像这样

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

值得一说的是 我们虽然这里将这两个函数设置为私有了 但是其实不管他们是公开还是私有结果都是一样的

在这里插入图片描述

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

override修饰虚函数 检查该虚函数是否被重写

如果被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/196258.html

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

相关文章

Java poi之Excel文本图片内容提取

目录结构前言文档准备引入Maven依赖代码块提取结果验证excel03.xls 提取结果excel07.xlsx 提取结果前言 应公司需求&#xff0c;需实现以下功能 Excel文本内容的替换&#xff1b;Excel文本内容的提取&#xff1b;Excel中图片的提取存放 此文章将使用Apache POI实现Excel文件…

我问 ChatGPT:怎样成为优秀的架构师?看它怎么回答的……

要成为一名优秀的架构师,需要以下几个方面的努力: 1. 系统的学习计算机科学和工程相关的知识,如计算机网络,数据结构,算法,操作系统等。 2. 实践和经验积累。参与许多实际的项目,不断积累经验,提高解决问题的能力。 3. 持续学习和追求卓越。保持对新技术和趋势的敏锐观…

Docker容器 01

前言 1.1 从环境配置说起 环境配置是软件开发的一大难题。开发、测试及运维人员需要相同的代码运行环境&#xff0c;如此一来就需要多次搭建环境&#xff0c;想想就觉得麻烦&#xff0c;实际上&#xff0c;在不了解docker等容器技术以前&#xff0c;还真就是这么干的&#xff…

IDEA 中动态web 工程的操作

目录a)IDEA 中如何创建动态web 工程1、创建一个新工程exer&#xff1a;2、在exer下创建module&#xff1a;test3、动态web工程创建成功 如下图b)Web 工程的目录介绍c)如何给动态 web 工程添加额外jar 包1 添加lib目录2 将jar包复制到lib目录中3 将jar包添加到工程4 可以打开项目…

西湖论剑2022部分misc

文章目录签到题喵take_the_zip_easymp3机你太美签到题喵 把文件尾的16进制复制出来&#xff0c;再转换字符串 私信后台即可获得flag take_the_zip_easy 明文攻击 echo -n “dasflow.pcapng” > 1.txt time ./bkcrack -C zipeasy.zip -c dasflow.zip -p 1.txt -o 30 -x 0…

六类网线为啥那么受欢迎,网络工程师必知

目前&#xff0c;国内千兆网络已大规模普及&#xff0c;从前的“销冠”百兆超五类网线已经渐渐淡出了人们的视野&#xff0c;已然被千兆的六类网线取代成为现代布线入门级主力军。万兆超六类网线也同时是城市智能化5G、万物互联时代入门首选&#xff0c;各自顺应时代成为不同领…

APP在Google Play上架被拒的原因

即便了解了Google Play商店的相关政策和应用指南&#xff0c;我们也不能避免应用在上架时或者是应用在更新时被拒的情况发生。那今天我们就展开讲讲Google Play商店被拒的原因及解决方案。 出现不当言论或内容&#xff08;比如&#xff0c;色情内容&#xff0c;带有种族歧视和…

基于Springboot搭建java项目(三十五)—— Ngnix配置的使用

Ngnix配置的使用 一、Nginx配置文件(nginx.conf) 1、配置文件的层级 配置文件目前分为三大部分&#xff0c;全局块、event块和http块&#xff0c;下面是具体的结构 2、配置文件概览 # 全局快 ---------------------------------------------------------------------------…

Bean实例化的基本流程

Bean实例化的基本流程 Bean实例化的基本流程-BeanDefinition Spring容器在进行初始化时&#xff0c;会将xml配置的的信息封装成一个BeanDefinition对象&#xff0c;所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去&#xff0c;Spring框架在对该Map进行遍历…

[NeurIPS 2018] Hyperbolic neural networks

ContentsIntroductionThe Geometry of the Poincar BallHyperbolic space: the Poincar ballGyrovector spaces (陀螺矢量空间)Mbius additionMbius scalar multiplicationDistanceHyperbolic trigonometryConnecting Gyrovector spaces and Riemannian geometry of the Poinca…

如何用提取网页内容的工具快速提取网站内容

随着社会的不断的进步&#xff0c;我们已经进入一个效率时代&#xff0c;相信每个人在互联网上下载或者复制粘贴过内容。特别是整理行业的数据&#xff0c;以及收集资料。今天小编就教大家如何用提取网页内容的工具快速提取到你想要的信息&#xff0c;只需要点几下鼠标就能提取…

GitHub2022年十大热门编程语言榜单

全球知名代码托管平台 GitHub发布的2022年GitHub Octoverse年度报告公布了全球最流行的十大编程语言&#xff0c;其中JavaScript蝉联第一&#xff0c;Python位列次席。 编程是技术革新的核心&#xff0c;对于所有的编程开发人员来说&#xff0c;对世界范围内编程语言发展和趋势…

磨金石教育摄影技能干货分享|人物系列摄影作品欣赏

人间烟火气&#xff0c;最能抚人心。生活中一些平平静静的瞬间&#xff0c;聊天、走路、欢笑&#xff0c;构成了人生当中闪闪的光。今天我们来欣赏一组充满烟火气的人物摄影。没有刻意的姿势&#xff0c;没有华丽的造景&#xff0c;有的就是真实与自然。《放学路上》小时候最欢…

linux 中的压缩和解压操作

1、压缩/解压操作 在开发中&#xff0c;很多时候会遇到某些文件要进行压缩的操作&#xff0c;比如文件较大不方便传输的时候&#xff0c;可能会考虑对文件进行压缩&#xff0c;以减少文件传输的时间。 比如在网络中传输文件的时候&#xff0c;就会考虑先将文件进行压缩&#xf…

微服务 过滤器 集成Sentinel实现网关限流

微服务 过滤器 集成Sentinel实现网关限流Gateway - -> 过滤器Filter局部路由过滤器使用局部过滤器全局过滤器使用全局过滤器集成Sentinel实现网关限流网关限流API分组限流Gateway - -> 过滤器Filter 过滤器就是在请求的传递过程中,对请求和响应做一些手脚. 在Gateway中, …

0xScope x Footprint | 真实的 NFT 市场是什么样?

2023 年 1 月数据源&#xff1a;NFT 真实交易分析面板前言NFT 作为一个2017年才出现的概念&#xff0c;在2018年至2020年一直处于生态发展的酝酿期&#xff0c;在2021年初开始迎来真正的爆发&#xff0c;一系列如CryptoPunk&#xff0c;The Sandbox&#xff0c;BAYC等知名NFT开…

Vue组件间通信的方式

目录 常用的父子组件通讯方式&#xff1a;props,emit 2.$parent,$children 3.$ref 4.provide/inject 5.EventBus 事件总线 &#xff08;任意两个组件通讯&#xff09; 6.$attrs、$listener 7.Vuex 状态管理器 8.localStorage/sessionStorage 在开发中&#xff0c;组…

相见恨晚,这6个适合安卓用户的浏览器,你用过吗

每个人手机里面有一款浏览器&#xff0c;当我们遇到问题的时候&#xff0c;可以打开浏览器搜索自己想要的答案。如果选用的手机浏览器不好&#xff0c;那么手机上可能会被安装很多垃圾软件。下面&#xff0c;和大家聊聊几款好用、适合安卓用户的浏览器&#xff0c;个人觉得这些…

【逐步剖C】第四章-操作符

​ 一、算术操作符 即基本的、-、*、/ 和 %。但也有几个需要注意的地方&#xff1a; 除了 ‘%’ 取模操作符只能作用整数&#xff0c;其他可以作用于整数和浮点数 对于除法&#xff0c;只要有操作数为浮点数就执行浮点数除法。如果两个操作数都为整数&#xff0c;执行整数除…

python多进程、多线程(详细)

多任务概念同一时间执行多个任务多任务优势最大的好处是充分利用CPU资源&#xff0c;提高程序的执行效率GIL锁&#xff08;全局解释锁&#xff09;让一个进程中同一个时刻只有一个线程可以被CPU调用&#xff0c;可以解决线程安全问题&#xff0c;有线程锁也有进程锁Rlock&#…