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

news2024/12/24 3:12:54

目录

(一)默认成员函数

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新的类功能全部知识。感谢大家的观看与支持!!!

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

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

相关文章

Shell脚本小试牛刀

都搞忘记了.....记录......... #!/bin/bash - # # # # FILE: countloop.sh # USAGE: ./countloop.sh # DESCRIPTION: # OPTIONS: ------- # REQUIREMENTS: --------- # # BUGS: ------ # …

02.sqlite3学习——嵌入式数据库的基本要求和SQLite3的安装

目录 嵌入式数据库的基本要求和SQLite3的安装 嵌入式数据库的基本要求 常见嵌入式数据库 sqlite3简介 SQLite3编程接口模型 ubuntu 22.04下的SQLite安装 嵌入式数据库的基本要求和SQLite3的安装 嵌入式数据库的基本要求 常见嵌入式数据库 sqlite3简介 SQLite3编程接口模…

Spark整合hive的时候出错

Spark整合hive的时候 连接Hdfs不从我hive所在的机器上找&#xff0c;而是去连接我的集群里的另外两台机器 但是我的集群没有开 所以下面就一直在retry 猜测&#xff1a; 出现这个错误的原因可能与core-site.xml和hdfs-site.xml有关&#xff0c;因为这里面配置了集群的nameno…

es的索引管理

概念 &#xff08;1&#xff09;集群&#xff08;Cluster&#xff09;&#xff1a; ES可以作为一个独立的单个搜索服务器。不过&#xff0c;为了处理大型数据集&#xff0c;实现容错和高可用性&#xff0c;ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。 &…

4.8 SYN什么时候被丢弃

TCP四次挥手过程中主动断开连接方有一个TIME_WAIT状态&#xff0c;这个状态会持续2MSL之后才会转变为CLOSED状态。一般一个MSL是30秒&#xff0c;所以以一共一般是60秒。这60秒内客户端会一直占用着端口。如果发起断开端的TIME-WAIT状态过多&#xff0c;占满了端口资源&#xf…

如何制作内部Wiki网站?

Wiki&#xff08;维基&#xff09;是一种协作工作的平台&#xff0c;也就是开源的编辑系统。我们可以使用维基建立一个帮助系统&#xff0c;知识库系统。在我国的公众wiki中&#xff0c;最出名的莫过于百度百科全书&#xff1b;本文将讨论的是企业的内部wiki。 企业维基&#…

探秘Linux系统性能监控神器!Linux和Python技术持续学习者必看!

引言 作为Linux运维工程师&#xff0c;我们经常需要对服务器的性能进行监控和调优。而Python作为一门强大的脚本语言&#xff0c;可以帮助我们轻松实现各种系统性能监控任务。本文将介绍几个实用的Python库和工具&#xff0c;帮助我们监控Linux系统的CPU、内存、磁盘和网络等性…

11.Oracle中rollup函数详解

【基本介绍】 【格式】&#xff1a;group by rollup(字段1,字段2,字段3,...,字段n) 【说明】&#xff1a;rollup主要用于分组汇总&#xff0c;如果rollup中有n个字段&#xff0c;则会分别按【字段1】、【字段1,字段2】&#xff0c;【字段1,字段2,字段3】&#xff0c;...&#…

Linux线程篇(中)

有了之前对线程的初步了解我们学习了什么是线程&#xff0c;线程的原理及其控制。这篇文章将继续讲解关于线程的内容以及重要的知识点。 线程的优缺点&#xff1a; 线程的缺点 在这里我们来谈一谈线程健壮性&#xff1a; 首先我们先思考一个问题&#xff0c;如果一个线程出现…

先进API生产力工具eqable HTTP,一站式开发调试工具推荐

简介 Reqable是什么? Regable Fiddler/Charles Postman Reqable是HTTP一站式开发调试国产化解决方案&#xff0c;拥有更便捷的体验&#xff0c;更先进的协议&#xff0c;更高效的性能和更精致的界面。 Reqable是一款跨平台的专业HTTP开发和调试工具&#xff0c;在全平台支持…

JVM调优与参数设置

JVM调优 1、开始 JVM调优不是常规手段&#xff0c;性能问题一般第一选择是优化程序&#xff0c;最后的选择才是进行JVM调优。 JVM的自动内存管理本来就是为了将开发人员从内存管理的泥潭里拉出来。即使不得不进行JVM调优&#xff0c;也绝对不能拍脑门就去调整参数&#xff…

2023年高教社杯 国赛数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…

Python 数据分析——matplotlib 快速绘图

matplotlib采用面向对象的技术来实现&#xff0c;因此组成图表的各个元素都是对象&#xff0c;在编写较大的应用程序时通过面向对象的方式使用matplotlib将更加有效。但是使用这种面向对象的调用接口进行绘图比较烦琐&#xff0c;因此matplotlib还提供了快速绘图的pyplot模块。…

iOS App签名与重签名:从开发者证书到重新安装运行

前文回顾&#xff1a; iOS脱壳技术&#xff08;二&#xff09;&#xff1a;深入探讨dumpdecrypted工具的高级使用方法 iOS逆向&#xff1a;越狱及相关概念的介绍 在本文中&#xff0c;我们将详细介绍iOS应用的签名过程&#xff0c;包括开发者证书的种类、证书与App ID、Provisi…

足球- EDA的历史数据分析并可视化

足球- EDA的历史数据分析并可视化 背景数据介绍探索数据时需要遵循的一些方向:数据处理导入库数据探索 数据可视化赛事分析主客场比分相关性分析时间序列分析 总结 背景 该数据集包括从1872年第一场正式比赛到2023年的44&#xff0c;341场国际足球比赛的结果。比赛范围从FIFA世…

GPIO输入-外电检测

前言 &#xff08;1&#xff09;本系列是基于STM32的项目笔记&#xff0c;内容涵盖了STM32各种外设的使用&#xff0c;由浅入深。 &#xff08;2&#xff09;小编使用的单片机是STM32F105RCT6&#xff0c;项目笔记基于小编的实际项目&#xff0c;但是博客中的内容适用于各种单片…

Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required问题解决

运行程序的时候出现了如下的报错&#xff1a; 解决方法&#xff1a;去除EnableAutoConfiguration中的(exclude{DataSourceAutoConfiguration.class})

淘宝API技术解析,实现关键词搜索淘宝商品(商品详情接口等)

淘宝提供了开放平台接口&#xff08;API&#xff09;来实现按图搜索淘宝商品的功能。您可以通过以下步骤来实现&#xff1a; 获取开放平台的访问权限&#xff1a;首先&#xff0c;您需要在淘宝开放平台创建一个应用&#xff0c;获取访问淘宝API的权限。具体的申请步骤和要求可以…

行业追踪,2023-08-25

自动复盘 2023-08-25 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…