拷贝构造函数与运算符重载

news2025/1/16 16:17:04

目录

一、拷贝构造函数

1.概念

2.特性

二、运算符重载 

1.运算符重载

2.运算符重载实现的形式 

3.赋值运算符重载 


一、拷贝构造函数

1.概念

拷贝构造函数是一种特殊的构造函数,它在创建对象时,使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于通过使用另一个同类型的对象来初始化新对象,或者在函数间传递对象时复制对象的状态。

拷贝构造函数只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

2.特性

①拷贝构造函数是构造函数的一个重载形式

拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器会直接报错,因为会引发无穷递归调用

③若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储字节序完成拷贝,这种拷贝叫做浅拷贝(值拷贝)

④编译器生成的默认拷贝构造函数中,内置类型直接拷贝,自定义类型调用其拷贝构造函数(与析构函数类似)

接下来对这些特性进行详细讲解:

关于第①点:

我们知道,构造函数的作用就是初始化,而我们在实例化出对象时是可以有多种初始化方式的,即构造函数支持重载,而拷贝构造函数就是将一个已经创建好的对象来初始化一个即将创建的对象,调用拷贝构造函数后这两个对象中的成员变量的值就是一样的。

关于第②点(重点):

我们先来看看拷贝构造函数的一般形式:

类名(const 类名& 其他对象)

参数为什么一定要是引用呢?直接传对象不可以吗?

我们知道,形参是实参的临时拷贝,如果是传值的话,在执行函数体中的语句前需要先拷贝实参给形参,而在拷贝构造函数中,参数是一个对象,要拷贝实参给形参就需要再次调用拷贝构造函数,而在这个函数中参数又是对象,就又要调用拷贝构造函数,就会一直这样无穷递归调用下去。

接下来看草图来帮助理解:

 在拷贝构造函数中,我们不希望所传入的参数(对象)被修改,所以要加上const。

关于第③点:

当我们没有写拷贝构造函数时编译器会自动生成一个拷贝构造函数,接下来我们看一段代码来验证:

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date today(2024, 4, 17);
	Date _today(today);
	today.Print();
	_today.Print();
	return 0;
}

运行结果: 

可以看到,_today最后初始化为与today一样了,说明调用了编译器自动生成的拷贝构造函数。

既然我们不写拷贝函数时,编译器可以帮助我们生成一个拷贝函数,那么是否我们就不需要写拷贝构造函数了呢?

答案肯定是否定的,因为编译器生成的拷贝构造函数只能实现浅拷贝,接下来就要详细讲解一下浅拷贝和深拷贝的区别了。

浅拷贝仅复制对象中的数据成员,而不涉及动态内存分配;深拷贝则会创建一个新的对象并在堆上分配内存,以复制原始对象中的动态内存部分。

也就是说,浅拷贝只能拷贝数据成员,如果对象申请了空间,是无法自动在堆区中申请空间的。

总结:

①如果不涉及到申请空间(资源),可以不用写拷贝构造,编译器自动生成的就可以

②如果全是自定义类型成员,内置类型成员没有指向资源,用生成的默认拷贝构造函数就可以

③如果内部有指针或者一些值指向资源,就需要显式写析构函数,一般需要显式写析构函数的就需要显式写拷贝构造函数

二、运算符重载 

1.运算符重载

运算符重载(Operator Overloading)是C++语言的一个强大特性,它允许程序员为自定义类型重新定义或重载已有的运算符,使得这些运算符能够用于自定义类型的对象。通过运算符重载,我们可以使得自定义类型的对象操作起来像内置类型(如int、float等)一样直观和方便。

运算符重载是具有特殊函数名的函数,也具有其返回类型,函数名字以及参数列表,其返回值类型是与参数列表与普通的函数类似。

函数名字为:关键字operator需要重载的运算符符号

函数原型:返回值类型 operator操作符(参数列表)

注意:

①重载运算符时,只能重载现有的运算符

②不能改变运算符的性质,如:运算符的优先级、结合性、操作数的个数及其语法结构

③五个不能重载的运算符:.   .*   ::   ?:   sizeof

2.运算符重载实现的形式 

要重载一个运算符,可以把该运算符函数定义为类的成员函数,也可以把该运算符函数定义为类的非成员函数。非成员函数的形式通常包括友元函数和全局函数等两种形式。一般来说,把运算符函数定义为类的成员函数和友元函数,具有更简单的成员设置要求和更高的成员访问效率。

大多数运算符可以同时用成员函数形式和友元函数形式重载,如加法、减法、乘法和除法等算术运算符。但是=  ()  []  ->只能重载为类的成员函数

需要注意的是,如果重载为成员函数,实际参数数要少一个,因为有一个隐含的this指针,指向当前对象。

例如有一个Date类,重载>并且将其作为成员函数,实际只需要传一个参数:

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator>(const Date& x)
	{
		//年大就大
		if (_year > x._year)
		{
			return true;
		}
		else if (_year == x._year)
		{
			//年相同,月大就大
			if (_month > x._month)
			{
				return true;
			}
			else if (_month == x._month)
			{
				return _day > x._day;
			}
		}

		return false;
	}

3.赋值运算符重载 

同样,我们先把所需要的注意点放前边总结,然后再详细讲解。

①参数类型:const T&,传引用可以提高效率(T是类名)

②返回类型:T&,避免再一次拷贝,提高效率

③避免自赋值

④赋值运算符只能重载为类的成员函数

⑤我们平时使用的赋值运算符=是可以连等的,重载时也应该保留这个特点

⑥用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝,内置类型成员变量是直接赋值的,而自定义类型需要调用对应类的赋值运算符重载完成赋值

 关于第①②点:

在传参和值返回时会创建临时变量,也就是说,如果我们返回类型是类类型,那么就会再一次调用拷贝构造函数,这种是不必要的,所以用引用的形式返回更高效。

需要注意的是,并不是什么时候都可以用引用返回,出了作用域,返回对象还没有析构,就可以用引用返回,减少拷贝,如果返回对象已经析构,就不能用引用返回

我们来看下边的代码,在Test函数中,我们创建了一个日期对象d,并且返回它的引用,然后在主函数中用dd再来接收(相当于Date& tmp = d; Date& dd = tmp;tmp是d的别名,dd是tmp的别名,即dd也是d的别名),按道理说d是2024年4月19日,而Test返回的是引用即别名,dd应当与d相同才是,实际上并不是这样的。原因在于d在执行完Test函数后就被析构了,然后这块内存就会被释放回栈中,当我们再调用其他函数(在这个例子中是Add)时就可能会用到那块空间,这时候就会出现如下的情况:dd中的数据成员全是随机值。

 关于第④点:

赋值运算符如果不显式实现,编译器会生成一个默认的,此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,所以赋值运算符重载只能是类的成员函数。

关于第⑤点:

 对于基本数据类型,是支持连等的,例如有两个int类型的数据a、b,我们可以写a=b=1;赋值运算符=的运算顺序是从右至左,所以实际返回的是左操作数,即我们重载函数时也要返回左操作数,即*this。

接下来我们来实现赋值运算符重载:

在类中再加一个成员函数:

Date& operator=(const Date& x)
{
	if (this != &x)
	{
		_year = x._year;
		_month = x._month;
		_day = x._day;
	}
	return *this;
}

 关于第⑥点:

与拷贝构造函数非常类似,如果未涉及到资源管理,赋值运算符重载是否实现都可以,一旦涉及到资源管理就必须自己实现。

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

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

相关文章

Vitis HLS 学习笔记--scal 函数-探究

目录 1. Vitis HLS重器-Vitis_Libraries 2. 初识scal() 3. 函数具体实现 3.1 变量命名规则 3.2 t_ParEntries解释 3.3 流类型详解 3.4 双重循环 4. 总结 1. Vitis HLS重器-Vitis_Libraries 在深入探索Vitis HLS&#xff08;High-Level Synthesis&#xff09;的旅程中&…

【单调栈】力扣85.最大矩形

好久没更新了 ~ 我又回来啦&#xff01; 两个好消息&#xff1a; 我考上研了&#xff0c;收到拟录取通知啦&#xff01;开放 留言功能 了&#xff0c;小伙伴对于内容有什么疑问可以在文章底部评论&#xff0c;看到之后会及时回复大家的&#xff01; 前面更新过的算法&#x…

kafka的概念以及Zookeeper集群 + Kafka集群 +elfk集群

目录 zookeeper同步过程 分布式通知和协调 zookeeper同步过程 分布式通知和协调 准备 3 台服务器做 Zookeeper 集群 192.168.68.5 192.168.68.6 192.168.68.7 安装前准备 //关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 node1服务器&a…

了解8大Python小陷阱,深入理解Python

学习了解python常见的使用陷阱&#xff0c;避免二次踩坑 Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识&#xff0c;阅读代码几无障碍。 然而&#xff0c;就像其他语言一样&#xff0c;Python确实有一些古怪特殊的地方。本文将介…

ssm062会员管理系统+jsp

会员管理系统 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于会员管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了会员管理系统&#xff0c;它彻底改…

关于二级指针void**的一点问题与思考

前言 这两天写一个高并发内存池的项目时&#xff0c;遇到了一个关于二级指针的问题&#xff0c;剖析清楚后发觉有必要记录一下&#xff0c;这让我加深了对于C/C中指针的理解&#xff08;果然学到老活到老&#xff09;。 问题的分析 在我的内存池项目中&#xff0c;有一个需求…

2024华中杯C题光纤传感器平面曲线重建原创论文分享

大家好&#xff0c;从昨天肝到现在&#xff0c;终于完成了2024华中杯数学建模C题的完整论文啦。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 10 一、问题重述 12 二&#xff0e;问题分析 13 2.1问题一 13 2.2问题二 14 2.3问题三 14 三、模型假设 15 四、…

Spring Task 定时任务调度

一、概念 Spring Task 是 Spring 框架的一个组件&#xff0c;它为任务调度提供了支持&#xff0c;使得开发者能够创建后台任务或定期执行的任务。通过 Spring Task&#xff0c;您可以方便地在 Java 应用程序中实现定时任务&#xff0c;比如每天凌晨进行数据同步、每小时执行一…

day02-新增员工

day01 新增员工业务逻辑整理 EmployeeController.java PostMappingApiOperation("新增员工")public Result save(RequestBody EmployeeDTO employeeDTO){System.out.println("当前线程的ID:" Thread.currentThread().getId());log.info("新增员工&a…

2024年华中杯数模竞赛A题完整解析(附代码)

2024年华中杯数模竞赛A题 基于动态优化的太阳能路灯光伏板朝向以最大化能量收集研究摘要问题重述问题分析模型假设符号说明 代码问题一 完整资料获取 基于动态优化的太阳能路灯光伏板朝向以最大化能量收集研究 摘要 随着可再生能源技术的发展&#xff0c;太阳能作为一种清洁的…

2024新版淘宝客PHP网站源码

源码介绍 2024超好看的淘客PHP网站源码&#xff0c;可以做优惠券网站&#xff0c;上传服务器&#xff0c;访问首页进行安装 安装好了之后就可以使用了&#xff0c;将里面的信息配置成自己的就行 喜欢的朋友们拿去使用把 效果截图 源码下载 2024新版淘宝客网站源码

【云计算】云数据中心网络(七):负载均衡

《云网络》系列&#xff0c;共包含以下文章&#xff1a; 云网络是未来的网络基础设施云网络产品体系概述云数据中心网络&#xff08;一&#xff09;&#xff1a;VPC云数据中心网络&#xff08;二&#xff09;&#xff1a;弹性公网 IP云数据中心网络&#xff08;三&#xff09;…

MySQL数据库-优化慢查询

1、什么是慢查询&#xff1f; 慢查询就是SQL执行时间过长&#xff0c;严重影响用户体验的SQL查询语句。当它频繁出现时数据库的性能和稳定性都会受到威胁 慢查询是数据库性能瓶颈的常见原因&#xff0c;是指SQL执行时间超过阈值&#xff1b;可能由于复杂的连接、缺少索引、不恰…

保持领先:四个ChatGPT小技巧助你成为不可替代的数据分析师

在前文中&#xff0c;我们初步探讨了为何ChatGPT无法完全替代数据分析师的原因&#xff0c;而本文将深入探讨如何利用GPT辅助数据分析师提升工作效率。 **场景一&#xff1a;SQL数据提取** 许多数据分析师需使用SQL语言从数据库中抽取数据。尽管SQL操作简便&#xff0c;但编写…

类和对象-封装-设计案例1-立方体类

#include<bits/stdc.h> using namespace std; class Cube{public://设置长void setL(int l){m_Ll;} //获取长int getL(){return m_L;}//设置宽 void setW(int w){m_Ww;}//获取宽 int getW(){return m_W;}//设置高 void setH(int h){m_Hh;}//获取高int getH(){return m_H;…

在线拍卖系统|基于Springboot的在线拍卖系统设计与实现(源码+数据库+文档)

在线拍卖系统目录 基于Springboot的在线拍卖系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、前台&#xff1a; 2、后台 用户功能模块 5.2用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a…

VR全景展览——开启全新视界的虚拟展览体验

随着VR技术的不断发展和成熟&#xff0c;VR全景展览已经成为现代展览行业的一大亮点。通过模拟现实世界的场景&#xff0c;VR全景展览为用户提供了一个沉浸式的观展体验&#xff0c;使参观者能够跨越地理和时间限制&#xff0c;探索不同领域的展览。 一、VR全景展览的功能优势 …

合并有序表 (顺序存储 和 链式存储 方式实现)

代码详细解析: 合并有序表文章浏览阅读1.4k次&#xff0c;点赞6次&#xff0c;收藏7次。●假设有两个有序表 LA和LB , 将他们合并成一个有序表LC●要求不破坏原有的表 LA和 LB构思:把这两个表, 合成一个有序表 , 不是简简单单吗?就算是把他们先遍历不按顺序插入到表 C里面 , …

电机控制专题(二)——Sensorless之扩展反电动势EEMF

文章目录 电机控制专题(二)——Sensorless之扩展反电动势EEMF前言理论推导仿真验证总结参考文献 电机控制专题(二)——Sensorless之扩展反电动势EEMF 前言 总结下电机控制中的扩展反电动势模型。 纯小白&#xff0c;如有不当&#xff0c;轻喷&#xff0c;还请指出。 在得出E…

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…