【C++】—— c++11新的类功能

目录

(一)默认成员函数

1、 移动构造函数

 2、代码辅助理解

3、移动赋值运算符重载

(二)default关键字

(三)delete关键字

(四)委托构造函数

1、优势 

2、缺点

总结 


(一)默认成员函数

原来C++类中,有6个默认成员函数:

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

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

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

1、 移动构造函数

移动构造函数是C++中的特殊成员函数之一,用于以移动语义的方式构造新对象。它是在C++11中引入的,旨在提高对象的性能和效率。

移动构造函数通常用于在不进行资源拷贝的情况下将临时对象或者右值引用的对象的内容转移到新创建的对象中。通过移动构造函数,可以避免不必要的拷贝操作,提高代码的性能。


 2、代码辅助理解

接下来我们通过代码来尝试着学习相关的知识:

  • 首先,我先给出手动实现的string类,代码如下:
namespace zp
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(char* str)" << endl;

			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		// s1.swap(s2)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;

			string tmp(s._str);
			swap(tmp);
		}

		// 移动构造
		string(string&& s)
			:_str(nullptr)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;
			swap(s);
		}

		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);

			return *this;
		}

		// s1 = 将亡值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);

			return *this;
		}

		~string()
		{
			//cout << "~string()" << endl;

			delete[] _str;
			_str = nullptr;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		//string operator+=(char ch)
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string operator+(char ch)
		{
			string tmp(*this);
			tmp += ch;
			return tmp;
		}

		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};

	//const zp::string& to_string(int value)
	zp::string to_string(int value)
	{
		bool flag = true;
		if (value < 0)
		{
			flag = false;
			value = 0 - value;
		}

		zp::string str;
		while (value > 0)
		{
			int x = value % 10;
			value /= 10;

			str += ('0' + x);
		}

		if (flag == false)
		{
			str += '-';
		}

		std::reverse(str.begin(), str.end());
		return str;
	}
}

接下来,有这样的一段代码,我们对它进行运行分析:

 接下来,我们给出相关示例,把代码运行起来看最终我们的样例是调用的什么:

 【解释说明】

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

此时,我们把其中的析构函数进行相关的实现,在看最终的打印结果是什么:

  【解释说明】

  1. 使用 move 操作可以显式地将 s1转换为右值引用,并尝试将 s1 通过移动语义移动到 s3 中;
  2. 此时,我们可以发现,当我们手动的实现了一个析构函数之后,编译器就会对识别进行深拷贝操作;

【小结】

针对移动构造函数有一些需要注意的点如下:

  1. 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造;
  2. 默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造
     

这里解释一下为什么生成默认移动构造条件这么苛刻的问题:

  1. 编译器之所以把这个条件设计这么苛刻,因为它认为说你是你自己要实现的拷构构造和拷贝赋值还是析构,按理来说你这个类呢,就是一个深拷贝的类,那你是一个深拷贝的类呢,那这个时候移动赋值,它不知道咋处理比较好;
  2. 就比如说这有个指针,那这个指针我要把你的资源转移,就是你可以认为它自己把控不住,它不知道该咋编译做资源移动。其次这个指针指向的资源就一定要移动吗?我们认为这是不一定的,要看我们实际当中的需求是什么,它自己把控不住,这个时候他就不再给你自动生成了;
  3. 那什么时候他觉得他可以把控住呢?就是像刚才这样的,你没有实现析构,你不是深拷贝的类,它就可以把控住。

3、移动赋值运算符重载

移动赋值运算符重载的原理跟上述移动构造函数一样的。大家只需记住一个,另一个类似的就可以记住!!!


(二)default关键字

在C++11之前,如果在类的定义中没有显式声明默认构造函数、复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符时,编译器会自动生成这些函数的默认版本。然而,在C++11及以后的标准中,如果我们显式地定义了一个带有参数的构造函数、复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符,编译器将不再自动生成默认版本。

为了强制生成默认版本的函数,我们可以使用关键字 default。在类的定义内部,用 = default形式指定函数。这将告诉编译器生成该函数的默认版本。

  • 代码展示:


(三)delete关键字

当我们在类中手动定义了自定义构造函数、复制构造函数、移动构造函数、赋值运算符或移动赋值运算符时,编译器就不会再自动生成默认版本的这些函数。然而,在某些情况下,我们可能希望保留编译器自动生成的默认版本。

使用 delete 关键字可以告诉编译器生成该函数的默认版本,即恢复被手动定义函数覆盖的默认行为。

  • 代码展示:


(四)委托构造函数

委托构造函数是C++11引入的特性之一,它允许一个构造函数调用同一类的其他构造函数来完成对象的初始化。通过委托构造函数,可以减少代码的冗余、提高可维护性,并且确保初始化逻辑的一致性。

以下是委托构造函数的简要说明和示例:

1、优势 

  • 委托构造函数的语法:

委托构造函数使用特殊的语法来调用同一类的其他构造函数。它在成员初始化列表中使用冒号(:)后面的成员初始化器列表来调用其他构造函数。

class Test 
{
public:
	Test(int x, int y) :  // 构造函数1,委托给构造函数2
		Test(x, y, 0) {   // 委托构造函数
	}

	Test(int x, int y, int z) :
		// 构造函数2,实际完成对象初始化的构造函数
	{
		// 具体的初始化逻辑
	}
};

【解释说明】 

  • 这样,当我们使用委托构造函数创建对象时,只需调用适合的构造函数,并由该构造函数负责完成所有初始化工作;
  • 这样做可以避免在多个构造函数中复制相同的初始化代码,提高了代码的可维护性。

  • 委托构造函数的特点:

    • 委托构造函数的声明和定义位于同一类中,并且在其他构造函数的前面。
    • 委托构造函数不能有初始值列表,因为它的作用是将初始化任务委托给其他构造函数。
    • 委托构造函数可以有自己的成员初始化列表,用于初始化委托所使用的其他构造函数中未初始化的成员变量。

2、缺点

委托构造函数在使用时存在一种潜在的问题,称为"委托环"(delegation cycle)。委托环是指构造函数之间形成了循环的委托调用关系,导致无限递归或编译错误的情况。

下面是一个示例,展示了如何在不小心的情况下创建委托环的代码:

class Test 
{
public:
	Test(int x) {
		// 执行一些初始化操作
	}

	Test() : Test(0) { // 委托构造函数,调用了另一个构造函数
		// 其他逻辑
	}
};

【解释说明】

  1. 在上面的代码中,Test 类有一个带有参数的构造函数和一个不带参数的构造函数,而不带参数的构造函数使用委托构造函数调用了带有参数的构造函数。这看起来没有问题,但实际上却形成了委托环;
  2. 当创建一个不带参数的 Test 对象时,会调用不带参数的构造函数。然后,由于委托构造函数的存在,它又会调用带有参数的构造函数。然后,带有参数的构造函数又会调用不带参数的构造函数。这样就形成了循环,导致无限递归。

委托构造函数自身不应该包含其他的初始化语句,否则会导致重复初始化的问题

class Test 
{
public:
	Test(int value) 
		: Test(value, 0.0) 
	{
		// 错误!委托构造函数体中存在其他初始化语句
		tmp_ = 42;
	}

	Test(int value, double rate)
		: value_(value)
		, rate_(rate)
	{
		// 构造函数体...
	}

private:
	int value_;
	double rate_;
	int tmp_;
};

【解释说明】

  1. 在上述示例中,委托构造函数的体内存在对 tmp_ 的初始化,这将导致该变量在构造过程中被初始化两次,可能会产生不可预料的结果。
  2. 正确的做法是,在委托构造函数内部只进行调用,不要插入其他的初始化语句。

总结 

以上便是关于本期 c++11新的类功能全部知识。感谢大家的观看与支持!!!

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

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

相关文章

基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v7.0版已发布

关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架&#xff0c;超轻量级、高度提炼&#xff0c;一套API优雅支持 UDP 、TCP 、WebSocket 三种协议&#xff0c;支持 iOS、Android、H5、标准Java、小程序、Uniapp&#xff0c;服务端基于Netty编写。 工程…

Multisim软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 Multisim软件是一款电路仿真和设计软件&#xff0c;由美国国家仪器公司&#xff08;National Instruments&#xff09;开发。它提供了一个交互式的图形界面&#xff0c;使用户能够轻松地构建和仿真电路。以下是Multisim软件的详…

攻防世界-embarrass

原题 解题思路 搜索flag&#xff0c;结果搜不到。 换到kali里看。

【多模态】26、视觉-文本多模态任务超详细介绍 「CLIP/LSeg/ViLD/GLIP/ALBEF/BLIP/CoCa/BEIT」

文章目录 准备知识一、CLIP&#xff1a;不同模态简单对比的方法更适合于图文检索1.1 CLIP 在分割上的改进工作1.1.1 LSeg1.1.2 Group ViT 1.2 CLIP 在目标检测上的改进工作1.2.1 ViLD1.2.2 GLIPv11.2.3 GLIPv2 二、ViLT/ALBEF &#xff1a;多模态融合在 VQA/VR 任务中更重要三、…

mysql 命令行 执行sql文件

方法1 source source file.sql; file.sql : 绝对路径或 相对路径。 方法2 mysql -u xxx -p < file.sql 方法3 MySQLImport 工具 mysqlimport [options] database file_name 其中&#xff0c;database为要导入数据的数据库名&#xff0c;file_name为要导入的SQL文件名。还可以…

后端开发有哪几种语言? - 易智编译EaseEditing

后端开发是构建应用程序的一部分&#xff0c;负责处理服务器端的逻辑、数据库交互和数据处理。有许多编程语言可用于后端开发&#xff0c;以下是一些常见的后端开发语言&#xff1a; Java&#xff1a; Java是一种广泛使用的面向对象编程语言&#xff0c;具有强大的跨平台能力。…

(纯c)数据结构之------>链表(详解)

目录 一. 链表的定义 1.链表的结构. 2.为啥要存在链表及链表的优势. 二. 无头单向链表的常用接口 1.头插\尾插 2.头删\尾删 3.销毁链表/打印链表 4.在pos位置后插入一个值 5.消除pos位置后的值 6.查找链表中的值并且返回它的地址 7.创建一个动态开辟的结点 三.顺序表与链表…

MySQL事务的隔离级别

前置阅读 快速搭建 Linux 学习平台 一、事务的特性 对于事务&#xff0c;我觉得有一句英文描述的非常贴切&#xff1a;All or not, now or never. 事务 &#xff08;Transaction&#xff09;可以说是关系型数据库最重要的特性了。SQL 事务就是一个或者多个 SQL 语句的集合&a…

响应式布局bootstrap使用

响应式布局 学习目标 能够说出响应式原理 能够使媒体查询完成响应式导航 能够使用Bootstrap的栅格系统 能够使用bootstrap的响应式工具 1.响应式原理 1.1响应式开发原理 就是使用媒体查询针对不同宽度的设备进行布局和样式的设置,从而适配不同设备的目的 1.2响应式布局容器…

顺序表之初

欢迎来到我的&#xff1a;世界 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 线性表简介顺序表定义动态顺序表的初始化尾插头插Cheak 判断是否增容尾删&#xff1a;头删&#xff1a;打印在pos位置前插入x删除pos位置的值查…

centos7搭建apache作为文件站后,其他人无法访问解决办法

在公司内网的一个虚拟机上搭建了httpsd服务&#xff0c;准备作为内部小伙伴们的文件站&#xff0c;但是搭建好之后发现别的小伙伴是无法访问我机器的。 于是寻找一下原因&#xff0c;排查步骤如下&#xff1a; 1.netstat -lnp 和 ps aux 先看下端口和 服务情况 发现均正常 2.…

Linux(基础篇一)

Linux基础篇 Linux基础篇一1. Linux文件系统与目录结构1.1 Linux文件系统1.2 Linux目录结构 2. VI/VIM编辑器2.1 vi/vim是什么2.2 模式间的转换2.3 一般模式2.4 插入模式2.4.1 进入编辑模式2.4.2 退出编辑模式 2.5 命令模式 3. 网络配置3.1 网络连接模式3.2 修改静态ip3.3 配置…

python中的matplotlib画直方图(数据分析与可视化)

python中的matplotlib画直方图&#xff08;数据分析与可视化&#xff09; import numpy as np import pandas as pd import matplotlib.pyplot as pltpd.set_option("max_columns",None) plt.rcParams[font.sans-serif][SimHei] plt.rcParams[axes.unicode_minus]Fa…

Linux TCP协议——三次握手,四次挥手

一、TCP协议介绍 TCP协议是可靠的、面向连接的、基于字节流的传输层通信协议。 TCP的头部结构&#xff1a; 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;&#xff08;tcp是传输层的协议&#xff0c;端与端之间的数据传输&#xff0c;在TCP和UDP协议当中不会体现出I…

[Linux]命令行参数和进程优先级

[Linux]命令行参数和进程优先级 文章目录 [Linux]命令行参数和进程优先级命令行参数命令行参数的概念命令函参数的接收编写代码验证 进程优先级进程优先级的概念PRI and NI使用top指令修改nice值 命令行参数 命令行参数的概念 命令行参数是指用于运行程序时在命令行输入的参数…

软件设计师学习笔记3-CPU组成

目录 1.计算机结构 1.1计算机的外设与主机 1.2计算机各部分之间的联系(了解一下即可) 2.CPU结构 1.计算机结构 1.1计算机的外设与主机 1.2计算机各部分之间的联系(了解一下即可) 该图片来自希赛软考 注&#xff1a;黄色的是传递数据的数据总线&#xff0c;白色的是传递控…

AI新时代,英特尔如何加强产学研融合?

人工智能作为当前数字经济发展的核心驱动力&#xff0c;我们在关注AI技术发展之际&#xff0c;为发挥AI强大助力&#xff0c;更需进一步思考AI的科研、产业应用与人才培育的工作&#xff0c;推动产学研融合创新。 正如英特尔公司高级副总裁、英特尔中国区董事长王锐在刚结束的…

【C++】C/C++内存管理-new、delete

文章目录 一、C/C内存分布二、C/C中动态内存管理方式2.1 C语言中动态内存管理方式2.2 C内存管理方式 三、operator new和operator delete函数3.1 operator new和operator delete函数3.2 operator new与operator delete的类专属重载&#xff08;了解&#xff09; 四、new和delet…

算法与数据结构(九)--并查集

并查集是一种树型的数据结构&#xff0c;并查集可以高校地进行如下操作&#xff1a; *查询元素p和元素q是否在同一组 *合并元素p和元素q所在的组 一.并查集结构 并查集也是一种树型结构&#xff0c;这种树的要求比较简单&#xff1a;1.每个元素都唯一的对应一个结点&#xff…

基于SSM的小说网站的设计与实现(论文+源码)_kaic

目 录 1 绪论................................................................................................... 1 1.1 项目背景................................................................................................................ 1 1.2 发展历程..…