C++:运算符重载与类的赋值运算符重载函数

news2024/9/20 15:05:31

目录

章节知识架构

一.运算符重载

1. 运算符重载的基本概念

    代码段1

2.关于运算符重载的重要语法细则

二.运算符重载在类中的使用

三.类的默认成员函数:=重载函数(赋值运算符重载) 

1.自定义=重载函数

   代码段2

2.编译器默认生成的=重载函数 

四.前置++(--)和后置++(--)的重载


章节知识架构

                                                    

一.运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数

1. 运算符重载的基本概念

C++中定义运算符重载的关键字:operator 

  1. 运算符重载本质上是函数,具有返回值类型,函数名字以及参数列表,其返回值类型参数列表与普通的函数定义规则类似;
  2. 运算符重载函数函数名命名规则关键字operator + 需要重载的运算符符号(例如定义函数名operator>,表示>的运算符重载);
  3. 运算符重载函数形参个数必须和被重载运算符的目数相同:单目操作符的重载函数有且只有一个形参,双目操作符的重载函数有且只有两个形参.
  4. 运算符重载函数必须有一个类类型参数
  5. 运算符重载函数首部的一般形式:返回值类型+operator运算符+(参数列表);

比如:

现在有一个记录日期的类Date,我们想比较两个日期类对象所记录的日期大小,因此设计一个运算符 '>'的重载函数

函数返回类型定义为bool;bool为一个字节的整形,其只能为true(值1)或false(值为0)

函数名定义为: operator>

函数的形参表为:(const Date & date2 ,const Date & date2)

代码段1

#include <iostream>

using std::cout;
using std::cin;
using std::endl;





class Date         记录日期的类
{                    
public:
	Date(int day = 0, int month = 0,int year = 0)          Date的带参构造函数
	{
		_day = day;
		_month = month;
		_year = year;
	}

	int _day;
	int _month;
	int _year;
};

bool operator> (const Date& date1, const Date& date2)       将 > 运算符进行重载用于日期比较
{
	if (date1._year > date2._year)
	{
		return true;
	}
	else if (date1._year == date2._year && date1._month > date2._month)
	{
		return true;
	}
	else if (date1._year == date2._year && date1._month == date2._month && date1._day >                 
    date1._day)
	{
		return true;
	}
	return false;
}



int main()
{
	Date a(2021, 4, 23);
	Date b(2022, 1, 27);

	if (a > b)                      用运算符重载来比较两个日期对象
	{
		cout << "Date a > Date b" << endl;
	}
	else
	{
		cout << "Date a < Date b" << endl;
	}
	return 0;
}

C++编译其会根据运算符的操作数的类型来判断该运算符是否要调用重载以及调用哪种形式的重载,比如上述代码段主函数中的 a>b表达式:

2.关于运算符重载的重要语法细则

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 作为类成员运算符重载函数时,其形参个数看起来比运算符目数少1,因为类成员函数的第一个参数为隐藏的this指针;关于this指针:http://t.csdn.cn/hncvq
  • 赋值运算符重载(比如=,+=,-+,*=等等)只能作为类的成员函数不能重载成全局函数
  • '".* "    "::"    "sizeof"     "?:"    "."     注意以上5个运算符不能重载。

二.运算符重载在类中的使用

  • C++引入运算符重载是为了提高代码的可读性,允许我们构建复杂类对象之间的运算表达式。
  • 运算符重载必须有一个类类型形参,因此在运算符重载函数中我们难免要访问类的成员变量
  • 为了保证封装性,类的成员变量往往定义在类的私有域中。
  • 所以为了保证类对象的封装性的同时可以让运算符重载函数直接访问到类的成员变量,大多数情况下我们往往把运算符重载函数定义为类的成员函数(方法)

现在对代码段1进行优化:

把运算符重载函数定义为类的成员函数(方法)

运算符重载函数定义为类的成员函数时注意不要忽略this指针

#include <iostream>

using std::cout;
using std::cin;
using std::endl;

class Date         //记录日期的类
{
public:
	Date(int day = 0, int month = 0,int year = 0)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	bool operator> (const Date& date)       //将 > 运算符进行重载
	{
		if (_year > date._year)
		{
			return true;
		}
		else if (_year == date._year &&_month > date._month)
		{
			return true;
		}
		else if (_year == date._year && _month == date._month && _day > date._day)
		{
			return true;
		}
		return false;
	}
	
private:
	int _day;
	int _month;
	int _year;
};



int main()
{
	Date a(29, 4, 2020);
	Date b(27, 4, 2020);

	if (a > b)
	{
		cout << "Date a > Date b" << endl;
	}
	else
	{
		cout << "Date a < Date b" << endl;
	}
	return 0;
}

 

 

简单地观察一下 a>b表达式的汇编指令:

三.类的默认成员函数:=重载函数(赋值运算符重载) 

类的默认成员函数中有一个赋值运算符=的重载

如果我们没有在类中自定义一个赋值运算符=的重载,编译器会在类中自动生成一个默认的=运算符重载函数

1.自定义=重载函数

赋值运算符(=)只能重载成类的成员函数不能重载成全局函数

  • 原因:赋值运算符如果没有被我们定义,编译器会在类中(任何类中)生成一个默认的赋值运算符重载。此时若我们在类域外定义一个全局的赋值运算符重载,就会和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

自定义=重载函数:

现在Date类中实现一个=重载函数:如果我们在类中自定义了=重载函数,编译器便不会在编译阶段自动生成其默认的赋值运算符(=)重载

代码段2

using std::cout;
using std::cin;
using std::endl;





class Date         记录日期的类
{
public:
	Date(int day = 0, int month = 0,int year = 0)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	bool operator> (const Date& date)       将 > 运算符进行重载
	{
		if (_year > date._year)
		{
			return true;
		}
		else if (_year == date._year &&_month > date._month)
		{
			return true;
		}
		else if (_year == date._year && _month == date._month && _day > date._day)
		{
			return true;
		}
		return false;
	}
	
	Date& operator=(const Date& date)        将 = 运算符进行重载
	{
		_day = date._day;
		_month = date._month;
		_year = date._year;
		return (*this);
	}

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



int main()
{
	Date a(29, 4, 2020);
	Date b;
	Date c;

	c = b = a;                              用=的重载完成连续赋值

	if (a > b)
	{
		cout << "Date a > Date b" << endl;
	}
	else 
	{
		cout << "Date a <= Date b" << endl;
	}
	return 0;
}

可以试着观察一下代码段中c=b=a的汇编指令:

注意:=重载函数使用引用传参引用返回而不使用值传参值返回是为了提高代码的运行效率(值传递需要拷贝出临时的类对象)

参见:http://t.csdn.cn/Rc5aI

           http://t.csdn.cn/9tLyK

2.编译器默认生成的=重载函数 

编译器默认生成的=重载函数:

编译器默认生成=运算符重载函数的功能和代码段2中自定义=重载函数功能类似

可以简单验证一下:

将代码段2中等号重载的代码段注释掉

using std::cout;
using std::cin;
using std::endl;




class Date         //记录日期的类
{
public:
	Date(int day = 0, int month = 0,int year = 0)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	bool operator> (const Date& date)       //将 > 运算符进行重载
	{
		if (_year > date._year)
		{
			return true;
		}
		else if (_year == date._year &&_month > date._month)
		{
			return true;
		}
		else if (_year == date._year && _month == date._month && _day > date._day)
		{
			return true;
		}
		return false;
	}
	
	//Date& operator=(const Date& date)     //将等号重载的代码段注释掉
	//{
	//	if (this != &date)
	//	{
	//		_day = date._day;
	//		_month = date._month;
	//		_year = date._year;
	//	}

	//	return (*this);
	//}

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



int main()
{
	Date a(29, 4, 2020);
	Date b;
	Date c;

	c = b = a;

	if (a > b)
	{
		cout << "Date a > Date b" << endl;
	}
	else 
	{
		cout << "Date a <= Date b" << endl;
	}
	return 0;
}

如果,中有类对象成员变量,该编译器默认生成的=重载函数中会去调用其成员类对象的赋值运算符重载。 

比如:

using std::cout;
using std::cin;
using std::endl;


class subclass                        //定义一个被嵌套的类
{
public:
	subclass(int a = 100)             定义subclass的构造函数
	{
		_a = 100;
	}
	subclass& operator = (const subclass& date)       定义subclass的赋值运算符重载函数
	{
		_a = date._a;
		cout << "hello" << endl;                      打印一下表示该函数被调用
		return (*this);
	}
private:

	int _a;
};

class Date         //记录日期的类
{
public:
	Date(int day = 0, int month = 0,int year = 0,int subclass=0)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	//bool operator> (const Date& date)       //将 > 运算符进行重载
	//{
	//	if (_year > date._year)
	//	{
	//		return true;
	//	}
	//	else if (_year == date._year &&_month > date._month)
	//	{
	//		return true;
	//	}
	//	else if (_year == date._year && _month == date._month && _day > date._day)
	//	{
	//		return true;
	//	}
	//	return false;
	//}
	
	//Date& operator=(const Date& date)     //将等号重载的代码段注释掉
	//{
	//	if (this != &date)
	//	{
	//		_day = date._day;
	//		_month = date._month;
	//		_year = date._year;
	//	}

	//	return (*this);
	//}

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



int main()
{
	Date a(29, 4, 2020,100);
	Date b;
	Date c;

	c = b = a;

	//if (a > b)
	//{
	//	cout << "Date a > Date b" << endl;
	//}
	//else 
	//{
	//	cout << "Date a <= Date b" << endl;
	//}
	return 0;
}

可以通过汇编指令来观察这个过程:

  • 需要注意的是:类似于拷贝构造函数,编译器在类中默认生成的赋值运算符重载是以逐个字节进行拷贝的方式完成类对象赋值的(这种拷贝方式称为浅拷贝)。 
  • 因此编译器默认生成的赋值运算符重载不能用于一些申请了额外内存资源类对象之间的拷贝赋值。(比如栈对象之间的拷贝赋值)

如果栈对象之间使用编译器默认生成的=重载函数,则会出现以下情形:

所以涉及到内存资源管理的类对象之间只能使用用户自定义的=重载完成深拷贝

四.前置++(--)和后置++(--)的重载

 针对Date类,我们来实现一下表示日期自增的++重载(日期增加一天)

using std::cout;
using std::cin;
using std::endl;


//class subclass                        //定义一个被嵌套的类
//{
//public:
//	subclass(int a = 100)             //定义subclass的构造函数
//	{
//		_a = 100;
//	}
//	subclass& operator = (const subclass& date)       //定义subclass的赋值运算符重载函数
//	{
//		_a = date._a;
//		cout << "hello" << endl;                      //打印一下表示该函数被调用
//		return (*this);
//	}
//private:
//
//	int _a;
//};

class Date         //记录日期的类
{
public:
	Date(int day = 0, int month = 0, int year = 0)   //Date的构造函数
	{
		_day = day;
		_month = month;
		_year = year;
	}

	//bool operator> (const Date& date)       //将 > 运算符进行重载
	//{
	//	if (_year > date._year)
	//	{
	//		return true;
	//	}
	//	else if (_year == date._year &&_month > date._month)
	//	{
	//		return true;
	//	}
	//	else if (_year == date._year && _month == date._month && _day > date._day)
	//	{
	//		return true;
	//	}
	//	return false;
	//}

	//Date& operator=(const Date& date)     
	//{
	//	if (this != &date)
	//	{
	//		_day = date._day;
	//		_month = date._month;
	//		_year = date._year;
	//	}

	//	return (*this);
	//}
	Date& operator++()             //实现日期类的前置++
	{
		_day++;
		return (*this);
	}

	Date operator++(int)           //实现日期类的后置++
	{
		Date tem(*this);
		_day++;
		return tem;
	}

	void Print()                    //类对象的日期打印函数
	{
		cout << _year << ' ' << _month << ' ' << _day << ' ' << endl;
	}

private:
	int _day;
	int _month;
	int _year;

};



int main()
{
	Date a(5, 4, 2020);
	a.Print();
	Date ret1(a++);
	ret1.Print();
	Date ret2(++a);
	ret2.Print();
	return 0;
}

代码图解:

注意一个语法细则:

前置--和后置--的重载和++类似。

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

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

相关文章

Facebook小组与主页:哪个更适合SEO?

在 SEO中&#xff0c;对于优化人员来说有两种策略&#xff1a;一种是在 Facebook组上投放广告&#xff1b;另一种则是在主页上投放广告。那么&#xff0c;这两种策略哪种更好呢&#xff1f;对于 SEO来说又有什么影响呢&#xff1f;如果你已经在 Facebook上进行了一些优化工作&a…

Python---文件操作

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 文件操作思维导图前言文件是什么文件路径文件操…

SpringBoot基础回顾:场景启动器

上一章我们回顾了 SpringBoot 的自动装配&#xff0c;以及承载自动装配的核心——自动配置类。自动配置类的定义位置通常在每个场景的 jar 包中&#xff0c;配置 spring.factories 文件中 EnableAutoConfiguration 的位置通常在相应的 autoconfigure jar 包下。本章会着重回顾和…

SpringMVC简介

SpringMVC简介什么是MVC?MVC的工作流程什么是SpringMVC&#xff1f;HelloWorld创建maven工程配置web.xml创建请求控制器配置springMVC.xml配置文件什么是MVC? MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分 M:Model,模型层,指工程中的javaBean,作用是是处理数…

恶意代码分析实战 9 隐蔽的恶意代码启动

9.1 Lab12-1 分析 查看程序的导入函数。 通过这几个函数&#xff0c;可以推断出是远程线程注入。 使用ProMon检测&#xff0c;并没有看到什么有用的信息。 使用Proexproer检查。 也没有什么有用的信息。 拖入IDA中分析一下。 将这几个字符串重命名&#xff0c;便于识别。 …

【MyBatis】| MyBatis使用⼩技巧

目录 一&#xff1a;MyBatis使用⼩技巧 1. #{}和${} 2. typeAliases 3. mappers 4. IDEA配置⽂件模板 5. 插⼊数据时获取⾃动⽣成的主键 一&#xff1a;MyBatis使用⼩技巧 1. #{}和${} #{}&#xff1a;先编译sql语句&#xff0c;再给占位符传值&#xff0c;底层是Prepar…

【C语言进阶】一文带你学会C语言文件操作

前言 我们前面学习结构体时&#xff0c;写了通讯录的程序&#xff0c;当通讯录运行起来的时候&#xff0c;可以给通讯录中增加、删除数据&#xff0c;此时数据是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯录中的数据自然就不存在了&#xff0c;等下次运行通讯录…

Python---自动生成二维码

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 自动生成二维码 二维码的本质上&#xff0c;就…

人工智能学习06--pytorch05--torchvision中的数据集使用DataLoader的使用

torchvision中的数据集使用 test_set的class属性 把数据集每一部分都变成tensor类型 现在输出的就是tensor数据类型了 DataLoader的使用 batch_size 一摞牌中&#xff0c;每次抓几张shuffle 打乱&#xff0c;第二次打牌前&#xff0c;牌的顺序要跟第一次不一样&#xff0…

【JavaSE】一文看懂构造器/构造方法(Cunstructor)

&#x1f331;博主简介&#xff1a;大一计科生&#xff0c;努力学习Java中!热爱写博客~预备程序媛 &#x1f4dc;所属专栏&#xff1a;Java冒险记【从小白到大佬之路】 ✈往期博文回顾: 【JavaSE】保姆级教程|1万字10张图学会类与对象–建议收藏 &#x1f575;️‍♂️近期目标…

CSS边框、边距、轮廓(边框宽度/颜色/各边/简写属性/圆角边框/内外边距/高度宽度/框模型/轮廓宽度/颜色/属性/偏移)——万字长文|一文搞懂

目录 CSS边框 CSS 边框属性 CSS 边框样式 实例 CSS 边框宽度 实例 特定边的宽度 实例 CSS 边框颜色 实例 特定边框的颜色 实例 HEX 值 实例 RGB 值 实例 HSL 值 实例 CSS 边框 - 单独的边 实例 不同的边框样式 实例 它的工作原理是这样的&#xff1a; …

ROS学习寄录1

1 创建ROS工作空间 1.1 创建工作空间 &#xff08;1&#xff09;创建工作空间 mkdir catkin_ws &#xff08;2&#xff09;进入catkin_ws文件夹&#xff0c;然后创建一个src文件夹 cd catkin_ws mkdir src &#xff08;3&#xff09;进入src文件夹&#xff0c;生成CMakeL…

「自控原理」4.2 根轨迹法分析与校正

本节介绍利用根轨迹法分析系统性能发热方法 本节介绍根轨迹校正 文章目录利用根轨迹分析系统性能主导极点法增加零极点对系统的影响根轨迹校正串连超前校正原理与步骤超前校正例题串连滞后校正附加开环偶极子的作用原理与步骤滞后校正例题利用根轨迹分析系统性能 利用根轨迹分…

Oracle cloud vps实例配置访问

Oracle cloud vps实例配置访问创建一个免费配置的实例&#xff0c;并配置访问创建实例时&#xff0c;系统映像选择创建实例时候的ssh密钥配置子网&#xff0c;打开22端口使用工具登录服务器配置多个公钥&#xff0c;支持多个ssh私钥来登录登录vps实例修改登录用户和身份验证方式…

【接口】接口超时原因分析

接口超时的原因&#xff1a; 一、网络抖动 有可能是你的网络出现抖动、网页请求API接口、接口返回数据给网页丢包了。 二、被带宽占满 用户量暴增&#xff0c;服务器网络带宽被占满。 服务器带宽&#xff1a;一定时间内传输数据的大小&#xff0c;如&#xff1a;1s传输10M…

剑指Offer 第1天

第 1 天 栈与队列&#xff08;简单&#xff09; 剑指 Offer 09. 用两个栈实现队列 class CQueue { public: CQueue() {} void appendTail(int value) { s1.push(value); } int deleteHead() { while(!s1.empty()) { …

【Git :分布式版本控制工具】

【Git &#xff1a;分布式版本控制工具】 了解 Git 基本概念 能够概述 Git 工作流程 能够使用 Git 常用命令 熟悉 Git 代码托管服务 能够使用 IDEA 操作 Git 一、 概述 1. 开发中的实际场景 备份代码还原协同开发追溯问题代码的编写人和编写时间 2. 版本控制器的方式 集中式…

【数据结构】6.6 图的应用

文章目录生成树及其构造生成树的特点无向图的生成树6.6.1 最小生成树最小生成树及其典型应用MST性质构造最小生成树1. Prim(普里姆)算法2. Kruskal(克鲁斯卡尔)算法两种算法比较6.6.2 最短路径最短路径问题1. Dijkstra(迪杰斯特拉)算法迪杰斯特拉算法步骤2. Floyd(弗洛伊德)算法…

从零搭建一个组件库(二)创建代码规范

文章目录前言集成eslint1.安装2.替换默认解析器3.创建.eslintrc.yml配置文件4.创建忽略文件.eslintignore集成 prettier1.安装2.创建配置文件.prettierrc集成# commitizen1.安装2.修改package.json3.测试className的BEM规范1.安装2.BEM概述3.创建hooks函数4.使用hooks函数5.封装…

Vuex里面四个map方法(mapState、mapGetters、mapActions、mapMutation)

本章节主要讲述Vuex里面的四个优化代码的map方法&#xff0c;mapState、mapGetters、mapActions、mapMutation 一、store文件夹下面index.js主要内容&#xff0c;包含state(用于存储数据)、getters(计算属性)、mutatiions(加工数据)、actions(相应组件动作、写逻辑) 二、四个ma…