c++类与对象(二)——赋值运算符重载与取地址操作符重载

news2025/4/21 2:28:00

文章目录

  • 一.运算符重载
    • 1.运算符重载的概念
    • 2.实现Date类
      • (1)> < >= <= != 重载
      • (2)+= -= + - 重载
      • (3)前置++与后置++重载
      • (4)日期-日期的实现
      • (5)<< 与 >>重载
    • 2.默认成员函数——赋值运算符重载
  • 二.默认成员函数——取地址操作符重载
  • #. 补充知识点:const成员

前言:

本章将通过日期类的实现,深入学习运算符重载的实现方法。本章将完成6个默认成员函数中剩余3个——赋值运算符重载取地址操作符重载的学习。

一.运算符重载

1.运算符重载的概念

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

  • 函数名:关键字operator后面接需要重载的运算符符号
  • 函数原型
  • 返回值类型 operator操作符(参数列表)
  • bool operator==(Date d1,Date d2);

需要注意的是:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

2.实现Date类

定义一个Date类:

class Date
{
public:
	//构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		//判断日期是否合法
		//GetMonthDay()获取这个月的天数
		if (month > 0 && month < 13 &&
			(day > 0 && day <= GetMonthDay(year, month)))
		{
				_year = year;
				_month = month;
				_day = day;
		}
		else
		{
			cout << "日期非法" << endl;
		}
	}
private:
	int _year;//年
	int _month;//月
	int _day;//日
};

函数接口:

// 类里面短小函数,适合做内联的函数,直接是在类里面定义的
class Date
{
	// 友元函数声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:
	// 构造
	Date(int year = 0, int month = 0, int day = 0);
	void Print() const;
	// 当月的天数
	int GetMonthDay(int year, int month) const;
	
	// 重载日期类与日期类运算==, !=, <, <=, >, >=
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;
	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	
	// 重载日期类与天运算+=, +, -=, -,
	Date& operator+=(int day);
	Date operator+(int day) const;
	Date& operator-=(int day);
	Date operator-(int day) const;

	// 重载日期类与日期类运算-
	int operator-(const Date& d) const;
	
	// 赋值运算符重载
	Date& operator=(const Date& d);

	// 重载日期类前置++--,后置++--
	Date& operator++();
	// int参数 仅仅是为了占位,跟前置重载区分
	Date operator++(int);
	Date& operator--();
	Date operator--(int);

	//取地址重载
	Date* operator&();
	const Date* operator&() const;

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

inline ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日";
	return out;
}

inline istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

在实现运算符重载的时候,有一点需要特别注意

二元运算符的重载函数的参数有两个,规定第一个参数左操作数第二个参数右操作数

在前面章节我们讲过成员函数的特性,成员函数有一个自带的参数this,类型为类类型。所以我们可以省略第一个参数,只写第二个参数

所以:

(1)> < >= <= != 重载

先实现两个运算符重载函数,其它的就可以复用已经实现好的运算符。

class Date
{
public:
	//构造函数
	//...
	bool operator==(const Date& d)
	{
		return (_year == d._year) && (_month == d._month) && (_day == d._day);
	}
	bool operator<(const Date& d) 
	{
		return _year < d._year
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator<=(const Date& d) 
	{
		//函数的复用
		return *this < d || *this == d;
	}

	bool operator>(const Date& d) 
	{
		//函数的复用
		return !(*this <= d);
	}

	bool operator>=(const Date& d) 
	{
		//函数的复用
		return !(*this < d);
	}

	bool operator!=(const Date& d) 
	{
		//函数的复用
		return !(*this == d);
	}
//...
};

(2)+= -= + - 重载

注意:下面四个运算符重载的右操作数都为day天数

class Date
{
public:
	//...
	//获取当月的天数
	int GetMonthDay(int year, int month) 
	{
		assert(month > 0 && month < 13);

		int monthArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		//判断是否是闰年的二月
		if (month == 2 && 
		((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0))
		{
			return 29;
		}
		else
		{
			return monthArray[month];
		}
	}
	//+= 返回自身的引用,减少拷贝
	Date& operator+=(int day)
	{
		//判断是否加了负数
		if (day < 0)
		{
			//复用
			*this -= -day;
			return *this;
		}

		_day += day;
		while (_day > GetMonthDay(_year, _month));
		{
			_day -= GetMonthDay(_year, _month);
			//进位
			_month++;
			if (_month == 13)
			{
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	//-= 返回自身的引用,减少拷贝
	Date& operator-=(int day)
	{
		//判断是否减了一个负数
		if (day < 0)
		{
			//复用
			*this += -day;
			return *this;
		}

		_day -= day;
		while (_day <= 0)
		{
			--_month;
			if (_month == 0)
			{
				--_year;
				_month = 12;
			}

			_day += GetMonthDay(_year, _month);
		}

		return *this;
	}
	Date operator+(int day) 
	{
		//拷贝构造
		//因为加不改变自身的值,所以创建临时对象
		Date tmp(*this);
		//复用
		tmp += day;
		return tmp;
	}
	Date operator-(int day)
	{
		Date tmp(*this);
		tmp -= day;
		return tmp;
	}
//...
};

(3)前置++与后置++重载

前置++与后置++都是一元运算符,这二者的区别是:

  • 前置++++再使用返回++之后的数
  • 后置++先使用再++,返回++之前的数

为了能在重载的时候做出区分,·C++·规定:

  • 后置++重载多增加一个int类型的参数,但调用时该参数不用传递,编译器会自动传递。
class Date
{
public:
	//...
	//前置++
	Date& operator++()
	{
		*this += 1;
		return *this;
	}
	//后置++
	// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,
	// 故需在实现时需要先将this保存一份,然后给this + 1
	// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
	Date operator++(int)
	{
		Date tmp(*this);
		*this += 1;
		return tmp;
	}
	//前置--
	Date& operator--()
	{
		*this -= 1;
		return *this;
	}
	//后置--
	Date operator--(int)
	{
		Date tmp(*this);
		*this -= 1;
		return tmp;
	}
//...
};

(4)日期-日期的实现

日期+日期没有意义,但是日期-日期有意义,日期-日期代表两日期相距多少天

class Date
{
//...
	int operator-(const Date& d)
	{
		Date max = *this;
		Date min = d;
		int flag = 1;

		if (*this < d)
		{
			max = d;
			min = *this;
			flag = -1;
		}

		int n = 0;
		while (min != max)
		{
			++min;
			++n;
		}

		return n * flag;
	}
	//...
}

(5)<< 与 >>重载

Date d1(2023,5,1);
cout<<d1;
  1. <<>>是二元操作符,上文提到二元操作符第一个参数为左操作符,第二个参数为右操作符
  2. 因为类中成员函数第一个参数为this,所以左操作数就成了对象,右操作数变成了cout,这就成了d1<<cout,与平常使用的C++语法习惯不符,所以我们不能将<<>>写到类的成员函数中,而是重载在类外面
  3. 但是类外的函数无法访问类的私有函数,所以我们将重载函数设置为友元函数来实现。
class Date
{
//...
	//申明友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
	//...
}
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日";
	return out;
}

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

2.默认成员函数——赋值运算符重载

与之前的构造函数析构函数等默认成员函数相同,赋值运算符重载也是类的6个默认成员函数之一

赋值运算符重载具有以下特性:

  1. 赋值运算符重载格式:
    参数类型const &T,参数引用可以提高传参效率
    返回值类型T&返回值引用可以提高返回的效率,有返回值目的是为了支持连续赋值,检测是否给自己连续赋值
    返回*this:要复合连续赋值的定义
  2. 赋值运算符只能重载成类的成员函数不能重载成全局函数
class Date
{
//...
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	//...
}

二.默认成员函数——取地址操作符重载

6个默认成员函数只剩两个——取地址重载与const取地址重载。但是,这两个函数实在没有实现的必要,因为我们自己实现与编译器自动实现出来的效果是一样的。

class Date
{
	//...
	Date* operator&()
	{
		return this;
	}
	const Date* operator&()const
	{
		return this;
	}
	//...
};

#. 补充知识点:const成员

const修饰的成员函数称之为const成员函数,const修饰类成员函数,实际修饰的该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

例如:

class Date
{
public:
	//...
	void print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
}

void Test3()
{
	Date d1(2023, 4, 1);
	d1.print();
	const Date d2(2022, 3, 1);
	d2.print();
}

运行结果:

在这里插入图片描述
这是因为权限放大了:我们不能将const Date &d2传递给形参Date* this
正确写法为:

void print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

无法显示的修饰隐含参数*this,所以在函数后面加上const修饰。这样做适合不在成员函数内修改成员变量的函数,对无const修饰的类同样适用。


本文到此结束,码文不易,还请多多支持哦!

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

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

相关文章

代码随想录之额外题目

数组 1207 独一无二的出现次数 看数组的大小和长度都没有很大&#xff0c;所以可以直接用数组来做哈希表&#xff0c;用一个数组来记录出现次数&#xff0c;再用一个数组来标记出现次数的值是否出现过。就是O(n) class Solution {public boolean uniqueOccurrences(int[] arr…

Spring IOC:IOC在Spring底层中如何实现?

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 Maven版本&#xff1a;apache-maven-3.6.3 Mybatis版本&#xff1a;3.5.6 spring版本&#xff1a;5.3.1 文章目录 Spring系列专栏文章目录一. 什么是IOC?二. IOC在spring中的实现2.1…

java 基础

第一章 计算机认识 1 概述 计算机包括**硬件&#xff08;hardware&#xff09;和软件&#xff08;software&#xff09;**两部分。硬件包括计算机可以看得见的物理部分&#xff0c;而软件提供看不见的指令。 2 计算机硬件介绍 3 计算机硬件——中央处理器 中央处理器&#xff0…

PyQGIS 加载单个shp文件到图层面板

打开QGIS Desktop 3.22.16&#xff0c;点击菜单栏 【设置】——>【Python控制台】 在Python控制台中点击【显示编辑器】按钮&#xff0c;打开Python编辑器 点击第一个按钮 【打开脚本文件】&#xff0c;选择加载图层列表到图层面板源码 # 加载图层列表到图层面板中 from qgi…

DataGridXL中快速搜索单元格和底部全屏模式区域隐藏

DataGridXL表格是在2020年发布&#xff0c;DataGridXL在设计时就考虑到了性能。提供最快、最简单、最可靠的数据网格。DataGridXL支持所有常用所有的浏览器&#xff0c;为 Web 应用程序提供类似于 Microsoft Excel 的体验&#xff0c;它支持前端框架有Vue、React、Angular等。 …

Acid burn(★★)

运行程序 先是弹出一个neg 然后是真正的程序界面 有一个输入Serial和Name的判断 还有一个只输入Serial的判断 查壳 没有壳&#xff0c;是Delphi程序 先除去一个Neg 找到Neg弹出的程序&#xff0c;在程序头下个断&#xff0c;运行程序&#xff0c;此时栈顶是调用此功能的…

希亦、米家、必胜家用洗地机测评,洗地机十年老用户告诉你哪款好用

如今&#xff0c;各种清洁设备早已进入我们的生活。其中&#xff0c;各种扫地机和洗地机更是出现在各大商场、酒店、餐饮、医院等领域。 洗地机是一种集洗地、吸尘于一体的清洗设备&#xff0c;它适合清扫如粉尘、烟头、纸屑、厨余、毛发等垃圾。它的工作效率高&#xff0c;可…

如何解决 :libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found

在使用 python 以下的命令时&#xff0c; from scipy.signal import convolve出现报错&#xff1a; /home/anaconda3/envs/norm/lib/python3.9/site-packages/scipy/linalg/../../../../libstdc.so.6: version GLIBCXX_3.4.30 not found (required by /home/anaconda3/envs/no…

上海车展有哪些让人过目不忘的电驱技术?

前言 2023年的第二十届上海国际汽车工业展览会&#xff08;以下简称“上海车展”&#xff09;中&#xff0c;扁线、SiC、800V、油冷成为了大多数车企电驱动力总成的标配。下面选取几家非常有特色的电驱技术进行解析&#xff0c;并探讨下电驱技术的发展方向。 舍弗勒对标件电驱…

前几天面了个32岁的测试员,年薪50w问题基本都能回答上,应该刷了不少八股文···

互联网行业竞争是一年比一年严峻&#xff0c;作为测试工程师的我们唯有不停地学习&#xff0c;不断的提升自己才能保证自己的核心竞争力从而拿到更好的薪水&#xff0c;进入心仪的企业&#xff08;阿里、字节、美团、腾讯等大厂.....&#xff09; 所以&#xff0c;大家就迎来了…

2023.05.09-使用AI克隆孙燕姿的声音来进行唱歌

文章目录 1. 简介 2. 资源合集 3. 准备数据集3.1. 人声分离3.2. 音频进行切片化处理3.2.1. 3.3. 数据集存放格式要求 4. 训练4.1. 启动web UI.bat 4.2. 识别数据集4.3. 数据预处理4.4. 设置训练超参数4.4.1. 选择模型分支 4.5. 进行训练4.5.1. 关于显存的说明 5. 推理5.1. 加载…

从bootanimation出发分析OpenHarmony下Gralloc buffer管理机制

从bootanimation出发分析OpenHarmony下Gralloc buffer管理机制 引言 这个文档主要记录从bootanimation角度出发&#xff0c;分析OpenHarmony下对gralloc buffer的管理&#xff01;由于OpenHarmony图形子系统过于复杂&#xff0c;且个人由于能力有限&#xff0c;这里我仅从grall…

玩机搞机--定制系统 隐藏app桌面图标 反编译app【二】

在定制rom的过程中。客户要求内置某些app。个别需求内置的app不能显示在桌面&#xff0c;那么对应的内置app方法就不做细阐述。可以参考原固件操作 那么今天主要讲下隐藏app的操作解析 &#x1f494;&#x1f494;&#x1f494;&#x1f494;&#x1f494;&#x1f494;&#…

C++知识点 -- C++的类型转换

C知识点 – C的类型转换 文章目录 C知识点 -- C的类型转换一、C语言中的类型转换二、C的强制类型转换1.static_cast2.reinterpret_cast3.const_cast4.dynamic_cast 一、C语言中的类型转换 void test() {int i 0;//隐式类型转换&#xff08;意义相近的类型&#xff09;double …

只下载rpm包而不安装(用于内网虚拟机使用)

这里写目录标题 问题&#xff1a;解决&#xff1a;1. 安装yum-utils2. 下载rpm包3. 将rpm包拷贝到离线的虚拟机并安装 问题&#xff1a; 公司虚拟机仅使用内网无法通过yum下载依赖&#xff1b; 解决方法之一就是从一台连了公网的虚拟机(NAT) 下载需要的rpm包&#xff0c;然后…

Xcode安装与配置

安装Xcode Xcode需要macOS系统上安装&#xff0c;截止到2020年1月31日&#xff0c;最新版本为11.3.1。你可以选择在苹果开发者网站下载Xcode安装文件或去苹果应用市场安装&#xff0c;无论哪种方式安装Xcode&#xff0c;都需要有一个自己的Apple ID&#xff0c;具体的申请注册流…

unittest 测试框架的使用

文章目录 1. unittest 框架解析2. 批量执行测试脚本1&#xff09;构建测试套件addTest() 方法makeSuite() 方法TestLoader() 方法 2&#xff09;用例的执行顺序3&#xff09;忽略测试用例的执行 3. unittest 断言4. HTML 报告生成5. 异常捕获与错误截图6. 数据驱动1&#xff09…

【项目测试报告】网页版博客系统 + online 好友聊天室

网页版博客系统 online 好友聊天室&#xff08;项目介绍&#xff09; 实现一个网页版的博客系统&#xff0c;提供了一个技术文章论坛&#xff0c;同时也提供了用户之间在线交流的平台。 网页博客系统支持以下核心功能&#xff1a; 用户注册、登录、注销功能&#xff08;登录…

JAVAWeb11-服务器渲染技术 -JSP-02-EL表达式(会使用)

1. EL 表达式介绍 EL 表达式全称&#xff1a;Expression Language&#xff0c;是表达式语言EL 表达式主要是代替 jsp 页面的表达式脚本<%request.getAttribute("xx")%>EL 表达式输出数据时&#xff0c;比 jsp 的表达式脚本简洁EL 表达式基本语法&#xff1a; …

HAL库版FreeRTOS(下)

目录 FreeRTOS 事件标志组FreeRTOS 事件标志组简介FreeRTOS 事件标志组相关API 函数FreeRTOS 事件标志组实验功能设计软件设计下载验证 FreeRTOS 任务通知FreeRTOS 任务通知简介任务通知的优势任务通知的缺点 FreeRTOS 任务通知相关API 函数 FreeRTOS 事件标志组 事件标志组与…