C++:类的6大默认成员函数:赋值运算符重载

文章目录

  • 赋值运算符重载
    • 1.1 运算符重载的引用
    • 1.2 运算符重载的概念
    • 1.3 赋值运算符重载
    • 总结一下(赋值运算符)

赋值运算符重载

1.1 运算符重载的引用

有一个日期类Date:

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

int main()
{
Date d1(2024,1,28);
Date d2(2024,2,27);
}

如上述main函数,有时我们如果想去比较比较两个对象,该如何去比较呢?

首先肯定不能直接比较,因为这不是内置类型,编译器无法直接识别:
在这里插入图片描述
根据之前C语言的经验,我们可以利用bool函数来判断,比如我们要去比较两个对象是否相等,可以这样操作:

bool DateEqual(const Date& x, const Date& y)
{
    return x._year == y._year
     && x._month == y._month
   && x._day == y._day;
}

int main()
{
Date d1(2024,1,28);
Date d2(2024,2,27);
cout<<DateEqual(d1,d2)<<endl;
}

当两个日期类对象年月日都不相等时,bool函数就返回false(0);

那么如果是判断两个日期类对象大小关系呢?
我们实现一个函数比较小于:

bool DateLess(const Date& x, Date& y)
{
	if (x._year < y._year)
	{
		return true;
	}
	else if(x._year == y._year)
	{
		if (x._month < y._month)
		{
			return true;
		}
		else if(x._month == y._month)
		{
			if (x._day < y._day)
			{
				return true;
			}
		}
	}
	return false;
}

int main()
{
Date d1(2024,1,28);
Date d2(2024,2,27);
cout<<DateLess(d1,d2)<<endl;

这样去写当然不会有问题,不过有个取名字问题的小瑕疵,我们这里函数取名较为正规,其他读者一看基本就知道此函数的作用,可一旦乱取名字,就会变得很混乱。
所以,C++引用了一种新的函数------运算符重载。

1.2 运算符重载的概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似

函数名字为:关键字operator后面接需要重载的运算符符号
函数基本构成:返回值类型 operator操作符(参数列表)

注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@

2.重载操作符必须有一个类类型参数

3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

. :: sizeof ?: .*

注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

运用赋值运算符重载我们就可以将上述判断日期类对象大小的函数改为:

bool operator==(const Date& x, Date& y)
{
	return x._year == y._year
		&& x._month == y._month
		&& x._day == y._day;
} 
bool operator<(const Date& x, Date& y)
{
	if (x._year < y._year)
	{
		return true;
	}
	else if(x._year == y._year)
	{
		if (x._month < y._month)
		{
			return true;
		}
		else if(x._month == y._month)
		{
			if (x._day < y._day)
			{
				return true;
			}
		}
	}
	return false;
}

使用方式我们可以用以下两种:

在这里插入图片描述
还可以这样做:

在这里插入图片描述
不过第二种方法要注意加上括号,因为流插入的优先级高于 == 和 <


接着往下看,不知道老铁们可注意到,上述我们定义Date类的时候,是把内置类型放开的:
在这里插入图片描述
如果设为私有,类外面就无法访问了:
在这里插入图片描述
不过此种情况解决方法也很简单,除了将内置类型放开以外,这里更推荐2种方法:

1.第一种就是在类里面搞一个Get函数【Java通常就是这样处理的】

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

	int GetYear()
	{
		return _year;
	}

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


2.第二种方法就是将函数放到类里面

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

	bool operator==(const Date& x, Date& y)
	{
		return x._year == y._year
			&& x._month == y._month
			&& x._day == y._day;
	}

	bool operator<(const Date& x, Date& y)
	{
		if (x._year < y._year)
		{
			return true;
		}
		else if (x._year == y._year)
		{
			if (x._month < y._month)
			{
				return true;
			}
			else if (x._month == y._month)
			{
				if (x._day < y._day)
				{
					return true;
				}
			}
		}
		return false;
	}

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

可是这里编译器会报错:参数过多
因为成员函数有一个隐藏的this指针(参照注意点4)
在这里插入图片描述
因此我们可以将上述代码修改为:

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

	bool operator==(Date& y)
	{
		return _year == y._year
			&& _month == y._month
			&& _day == y._day;
	}

	bool operator<(Date& y)
	{
		if (_year < y._year)
		{
			return true;
		}
		else if (_year == y._year)
		{
			if (_month < y._month)
			{
				return true;
			}
			else if (_month == y._month)
			{
				if (_day < y._day)
				{
					return true;
				}
			}
		}
		return false;
	}

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

如下使用即可:
在这里插入图片描述

1.3 赋值运算符重载

赋值运算符重载也是6大默认成员函数之一(且是最重要的4个成员函数之一),前面几篇博客我们聊到了构造、析构、拷贝构造,从功能上来讲,赋值运算符重载和拷贝构造函数很像。
我们先写一个赋值运算符:

void operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}


我们先一起来看看两者有什么区别:
拷贝构造函数,本质是构造函数,它是一个已经存在的对象初始化要创建的同类型对象,就像:

Data d1(2024,1,19);
Data d2(2024,2,17);
Data d3(d1);

而赋值运算符重载是对两个已经存在的对象,一个拷贝赋值给另外一个:

d1=d2;
d1.Print();
d2.Print();

在这里插入图片描述
我们可以看到d1被赋值上了d2的值了~

  • 但是我们这样写又会遇到新的问题-------无法连续赋值

在这里插入图片描述

  • 这里d2=d3会去调用赋值运算符,想要连续为d1赋值,就要将d2作为d1=d2的右操作数
  • 返回类型用传引用返回(Date&),返回的是*this,不要写成this,因为我们要返回的是对象,this仅仅是个指针
Date& operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;

	return *this;
}

好了,连续赋值的问题咱们就这样解决了。

  • 但是不排除有人会有如下写法:d1=d1;

其实这样写语法也没有什么问题,但通常我们会在赋值重载里加上一个判断:

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

  • 注意:形参里面的&是引用,if判断里面的&为取地址,记得分清楚

  • 还有一点就是赋值运算符重载我们不写,编译器也会自动生成一个的

  • 内置类型会值拷贝,自定义类型会调用它的拷贝

总结一下(赋值运算符)

  1. 赋值运算符重载格式
  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

  1. 赋值运算符只能重载成类的成员函数不能重载成全局函数
class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 int _year;
 int _month;
 int _day;
};

Date& operator=(Date& left, const Date& right)
{
 if (&left != &right)
 {
 left._year = right._year;
 left._month = right._month;
 left._day = right._day;
 }
 return left;
}


// 编译失败:
// error C2801: “operator =”必须是非静态成员
  • 注意:赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
    原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
    在这里插入图片描述

预告:下篇博客我们将对默认成员函数收尾,涉及const成员函数及相关周边知识点,且完整实现Date类。
Byebye老铁们,下篇博客再述~

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

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

相关文章

Rust使用feature特性和条件编译,以及常用feature使用说明

Cargo Feature 是非常强大的机制&#xff0c;可以为大家提供条件编译和可选依赖的高级特性&#xff0c;可以为你省下不少的代码量来判断操作系统和条件编译等功能。rust官方条件编译文档&#xff1a;Conditional compilation - The Rust Reference features特性 Featuure 可以…

GeometryInstance点击改变颜色

目录 项目地址实现效果核心代码 项目地址 https://github.com/zhengjie9510/webgis-demo 实现效果 核心代码 // Draw different instances each with a unique color const rectangleInstance new Cesium.GeometryInstance({geometry: new Cesium.RectangleGeometry({recta…

EFCore的空迁移(EFCore操作已存在的数据库表,不影响其中的数据)

背景&#xff1a;EFCore默认的会自动创建数据表&#xff0c;但是有时又是DBFirst&#xff0c;数据库写好了要用现成的表。这个时候就需要进行一些特殊的操作了 1、写出跟要对接数据库的实体类 比如我的表是这样创建的 create table mail_test (user_id bigint auto_increment …

【Entity Framework】EF中DbSet类详解

【Entity Framework】EF中DbSet类详解 文章目录 【Entity Framework】EF中DbSet类详解一、概述二、定义DbSet2.1 具有DbSet属性的DbContext2.2 具有 IDbSet 属性的 DbContext 2.3 具有 IDbSet 属性的 DbContext三、DbSet属性四、DbSet方法五、DbContext动态生成DbSet 一、概述 …

医院消防巡检系统革新:凡尔码平台二维码技术引领安全升级

医院消防巡检&#xff0c;传统依赖手工记录&#xff0c;效率和准确性受限。凡尔码平台的二维码消防巡检系统&#xff0c;以创新技术颠覆传统&#xff0c;实现即时、精准的安全管理&#xff0c;确保医院消防安全无虞。 凡尔码平台的消防巡检系统不仅提升了医院安全管理的效率&a…

基于SpringBoot + Vue实现的校园失物招领系统设计与实现+毕业论文

介绍 系统包含用户和管理员两个角色 用户&#xff1a;登录、注册、留言板、公告信息、失物招领、失物认领、寻物启事、个人中心、我发布的失物信息、我的失物认领、我发布的寻物启事、寻物启事留言等功能。 管理员&#xff1a;登录、基础数据管理、系统管理、留言板管理、失物信…

Linux项目自动化构建工具-make/ makefile及其应用:多文件编写第一个linux程序:进度条(懒人学习必备博文!!!)

目录 1.前言--make/makefile的引入 2.快速上手make/makefile---自动化构建 3.关于依赖关系和依赖方法 4.自动化清理 为什么我们执行编译的时候&#xff0c;make一下就好&#xff0c;清理却要使用make clean? 5. make/makefile是如何知道当前目录下可执行文件是否为最新 6.文件…

SQLServer sys.default_constraints介绍

sys.default_constraints 是 SQL Server 的系统视图&#xff0c;它包含了数据库中所有默认约束的信息。默认约束是数据库对象&#xff08;如表中的列&#xff09;的约束&#xff0c;它为列定义了一个默认值&#xff0c;当在插入新行时没有为该列提供值时&#xff0c;将使用这个…

集合嵌套,Collections,斗地主案例,日志框架

文章目录 集合嵌套List嵌套ListList嵌套MapMap嵌套Map Collections类方法排序 sort 乱序 shuffle 斗地主案例需求思路代码 日志框架介绍优势体系结构Logback概述快速入门配置详解 集合嵌套 List嵌套List public static void main(String[] args){//一个年级有许多班级&#xf…

spring boot 整合j2cache 基础操作

spring boot 整合缓存的内容呢 已经学了好久了 那么 今天 我们开始学习 j2cache 这个技术 并不是一个缓存 而是一个框架 我们可以将其他缓存配到这套框架上来 那么 我们就还是弄最熟悉的 ehcache redis进行整合 首先 我们启动 redis 然后 我们打开项目 pom.xml 注入依赖 …

【2024系统架构设计】案例分析- 2 系统开发基础

目录 一 基础知识 二 真题 一 基础知识 1 结构化的需求分析 结构化特点:自顶向下,逐步分解,面向数据。 三大模型:

springboot通过threadLocal+参数解析器实现保存当前用户登录信息

首先先介绍一下threadLocal ThreadLocal 线程局部变量&#xff0c;创建一个线程变量后&#xff0c;针对这个变量可以让每个线程拥有自己的变量副本&#xff0c;每个线程是访问的自己的副本&#xff0c;与其他线程的相互独立。 大致知道threadLocal就可以了&#xff0c;然后我…

web基础07-Vue

目录 一、Vue 1.概述 2.MVC与MVVM 3.快速入门 4.Vue工程的创建 &#xff08;1&#xff09;基于vue-cli &#xff08;2&#xff09;基于Vite&#xff08;推荐&#xff09; 5.Vue3核心语法 6.setup &#xff08;1&#xff09;概述 &#xff08;2&#xff09;返回值方式…

CTF题型 php://filter特殊编码绕过小汇总

CTF题型 php://filter特殊编码绕过小汇总 文章目录 CTF题型 php://filter特殊编码绕过小汇总特殊编码base64编码string过滤器iconv字符集 例题1.[Newstarctf 2023 week2 include]2.[Ctfshow web 117] php://filter 是一个伪协议&#xff0c;它允许你读取经过过滤器处理的数据流…

Java中的Object类解析与应用探究

作为一名对技术充满热情的学习者&#xff0c;我一直以来都深刻地体会到知识的广度和深度。在这个不断演变的数字时代&#xff0c;我远非专家&#xff0c;而是一位不断追求进步的旅行者。通过这篇博客&#xff0c;我想分享我在某个领域的学习经验&#xff0c;与大家共同探讨、共…

基于单片机自行车码表系统设计

**单片机设计介绍&#xff0c;基于单片机自行车码表系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机自行车码表系统设计主要涵盖了硬件设计、软件设计以及功能实现等多个方面。以下是对该设计概要的详细描述&#xff1a…

Sql注入靶场环境搭建

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器…

全国青少年软件编程(Python)等级考试三级考试真题2023年12月——持续更新.....

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;三级&#xff09; 分数&#xff1a;100 题数&#xff1a;38 一、单选题(共25题&#xff0c;共50分) 1.一个非零的二进制正整数&#xff0c;在其末尾添加两个“0”&#xff0c;则该新数将是原数的&#xf…

PS从入门到精通视频各类教程整理全集,包含素材、作业等(3)

PS从入门到精通视频各类教程整理全集&#xff0c;包含素材、作业等 最新PS以及插件合集&#xff0c;可在我以往文章中找到 由于阿里云盘有分享次受限制和文件大小限制&#xff0c;今天先分享到这里&#xff0c;后续持续更新 中级教程 https://www.alipan.com/s/unii5YxtM8B 提…

【计算机考研】408复习资料汇总+分享

王道对于当作408的参考书还是不错的 所以&#xff0c;矮子里面挑高个&#xff0c;大家将就一下算了&#xff0c;很多人说天勤的讲义也可以&#xff0c;只能说你去用一下试试就知道了&#xff0c;天勤除了数据结构的代码还可以&#xff0c;其他的比不过王道的资料。所以&#x…