【C++ | 重载运算符】一文弄懂C++运算符重载,怎样声明、定义运算符,重载为友元函数

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-06-21 22:14:42

本文未经允许,不得转发!!!



在这里插入图片描述

🎄一、为什么需要重载运算符

正常情况下,C++ 的运算符( +、-、*、/ 等)只能用于对基本类型的常量或变量进行运算,而不能用于类对象之间的运算。

类的对象直接的运算虽然可以通过成员函数或全局函数去实现,如date.Add(1),但这样的写法有时不易理解。

C++的提供了 重载运算符 的特性,允许将一些常见的运算符根据自定义类型的需要去重新定义,尽量让对象之间的运算可以像基本类型的运算一样简单、好理解。

运算符重载的目的是使得 C++ 中的运算符也能够用来操作对象。

下面例子演示了CDate类对象 使用 Add函数与 重载运算符operator+ 的运算结果:

// g++ 19_operatorAdd.cpp 
#include <iostream>
using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	CDate Add(int day);					// Add成员函数声明
	CDate operator+(int day);			// 加号运算符声明
	void show()
	{
		cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	//cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	//cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}

// Add成员函数定义
CDate CDate::Add(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling CDate::Add" << ", this=" << &temp << endl;
	return temp;
}

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

int main()
{
	CDate date(2024,6,20);
	date.show();
	cout << endl;
	
	CDate date1 = date.Add(1);		// 成员函数
	date1.show();
	cout << endl;
	
	CDate date2 = date.operator+(1);// 直接调用运算符函数
	date2.show();
	cout << endl;
	
	CDate date3 = date + 1;			// 好理解
	date3.show();
	cout << endl;
	
	return 0;
}

运行结果如下:
在这里插入图片描述
例子中两个成员函数Addoperator+的实现是一样的,但三种调用方式中,date + 1会比较好理解。

例子中的date.operator+(1);等价于date + 1;在运算符表示法中,运算符左侧的对象(这里为 date)是调用对象,运算符右边的对象(这里为 1) 是作为参数被传递的对象


在这里插入图片描述

🎄二、怎样声明、定义重载运算符函数

重载运算符,本质上也是函数的一种,使用关键字operator加上运算符作为函数名,运算符函数的格式大致如下:

返回值类型 operator 运算符(形参列表)
{
	...
}

函数名是清楚了,那形参怎样写呢? 返回值类型应该是什么类型?这小节的剩下内容会解决这两个问题。

✨2.1 重载运算符的限制

清楚了重载运算符的大致格式之后,再来了解一下C++对重载运算符的限制:

  • 1、重载后的运算符必须至少有一个操作数是用户定义的类型(类、结构体、枚举),这样可以防止给基本类型重载运算符;
  • 2、使用运算符时不能违反运算符原来的句法规则,例如,不能将加号(+)重载为减法运算。也不能修改运算符优先级;
  • 3、不能创建新运算符,例如,不能定义 operator **( )函数来表示求幂。
  • 4、不能重载下面的运算符:
    • sizeof:sizeof 运算符。
    • .:成员运算符。
    • .*:成员指针运算符。
    • :::作用域解析运算符。
    • ?::条件运算符。
    • typeid:—个 RTTI 运算符。
    • const_cast:强制类型转换运算符。
    • dynamic_cast:强制类型转换运算符。
    • reinterpret_cast:强制类型转换运算符。
    • static_cast:强制类型转换运算符。
  • 5、下面的运算符只能通过成员函数进行重载
    • =: 赋值运算符。
    • ( ):函数调用运算符。
    • [ ]:下标运算符。
    • ->:通过指针访问类成员的运算符。

在这里插入图片描述


✨2.2 重载运算符定义

我们知道重载运算符的函数名是:operator运算符,那么形参个数和类型应该是什么?

重载运算符函数形参

重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。一元运算符有一个参数, 二元运算符有两个。对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。
如果一个运算符函数是成员函数, 则它的第一个(左侧)运算对象绑定到隐式的 this 指针,因此,成员运算符函数的(显式)参数数量比运算符的运算对象总数少一个。

从上面这段话可以知道,重载运算符函数的形参由运算符的操作数的个数、类型决定的。如果是成员函数,则左边操作对象的类型的形参会省略,以this指针传到运算符函数。以CDate类为例:

CDate date(2024,6,20);
CDate date1(2024,6,20);
CDate date2 = date + date1;	// 形参列表是:(const CDate &date, const CDate &date1)
CDate date2 = date + 1;		// 形参列表是:(const CDate &date, int i)
CDate date2 = date + 1.5;	// 形参列表是:(const CDate &date, double d)

重载运算符函数返回值类型
重载运算符的返回类型通常情况下应该与其内置版本的返回类型兼容:逻辑运算符和关系运算符应该返冋 bool,算术运算符应该返回一个类类型的值,赋值运算符和复合赋值运算符则应该返冋左侧运算对象的一个引用。

下面代码以重载加号运算符为例,这里是让一个CDate对象加上一个整型:

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

✨2.3 例子-重载赋值运算符

赋值运算符的重载也是C++的一个特别重要的知识点,可以重载为 拷贝赋值运算符 、移动赋值运算符 两种,需要详细了解的可以参考这两篇文章:
【C++ | 拷贝赋值运算符函数】一文了解C++的 拷贝赋值运算符函数
【C++ | 移动赋值运算符】一文了解C++11的 移动赋值运算符

这个小节只介绍怎样声明、定义赋值运算符,拷贝赋值运算符声明、定义如下。赋值运算符只能重载为函数名为operator=,其形参都是一个const CDate &类型的,返回值是该类型的引用(这样支持连续赋值a=b=c):

CDate& operator=(const CDate &date);// 拷贝赋值运算符声明
// 赋值运算符函数定义
CDate& CDate::operator=(const CDate& date)
{
	if(this == &date)	// 赋值给自身
		return *this;
	delete [] str;		// 释放旧的数据
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	cout << "Calling operator=" << ", this=" << this << ", Copy Data" <<endl;
	return *this;
}

在这里插入图片描述

🎄三、重载为友元函数

如果一个运算符的左操作数是类对象,则这个运算符一般是可以重载为成员函数的,例如上文的date+1。那如果表达式是1+date,则上面重载的加号运算符operator+匹配不到这个表达式。这是我们就需要将重载运算符写到类外部,而类外定义的函数无法访问类的私有成员,所以又需要将这个类外定义的函数声明为类的友元函数。关于C++的友元,有不了解的,可以参考这篇文章:C++ | 友元(friend)】友元函数、友元类、友元成员函数详解及例子代码

下面看看怎样将加号运算符重载为友元函数:

  • 1、看要实现怎样的操作。这里是要实现1+date
  • 2、确定重载运算符的形参,1+date的第一个操作数是int型,第二个操作数是CDate,所以形参可以是:(int i, const CDate &date)
  • 3、确定返回值类型,这里我们就直接返回 CDate 类,所以函数原型如下;
    CDate operator+(int day, const CDate &date);
    
  • 4、写具体函数实现,我们这里想要的是,让天数加上 i ,完整实现如下:
    // 友元函数定义
    CDate operator+(int day, const CDate &date)
    {
    	CDate temp = date;
    	temp.m_day += day;
    	cout << "Calling operator+(int, CDate)" << ", temp=" << &temp << endl;
    	return temp;
    }
    
  • 5、将函数声明为类的友元函数,在类声明开头使用friend如下声明:
    class CDate
    {
    	friend CDate operator+(int day, const CDate &date);	// 友元函数声明
    	...
    }
    

重载运算符时,优先重载为成员函数,如果不能重载为成员函数,就重载为友元函数。
编译器查找运算符函数的顺序是,先去类中查找匹配的成员函数,如果找不到就到全局函数寻找。


在这里插入图片描述

🎄四、总结

👉本文主要介绍了为什么需要重载运算符,重载运算符有哪些限制,怎样声明、定义运算符函数,怎样将运算符重载为友元函数。

上面例子的完整代码:

// g++ 19_operator.cpp 
#include <iostream>
using namespace std;

class CDate
{
	friend CDate operator+(int day, const CDate &date);	// 友元函数声明
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	CDate& operator=(const CDate &date);// 拷贝赋值运算符声明
	CDate operator+(int day);			// 加号运算符声明
	CDate operator+(const CDate& date);	// 加号运算符声明
	
	void show()
	{
		cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	//cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	//cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}

// 赋值运算符函数定义
CDate& CDate::operator=(const CDate& date)
{
	if(this == &date)	// 赋值给自身
		return *this;
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	cout << "Calling operator=" << ", this=" << this << ", Copy Data" <<endl;
	return *this;
}

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

// 加号运算符定义
CDate CDate::operator+(const CDate& date)
{
	CDate temp = *this;
	temp.m_day += date.m_day;
	cout << "Calling operator+(const CDate&)" << ", this=" << &temp << endl;
	return temp;
}

// 友元函数定义
CDate operator+(int day, const CDate &date)
{
	CDate temp = date;
	temp.m_day += day;
	cout << "Calling operator+(int, CDate)" << ", temp=" << &temp << endl;
	return temp;
}

int main()
{
	CDate date(2024,6,20);
	date.show();
	cout << endl;

	CDate date1 = date + 1;
	date1.show();
	cout << endl;
	
	CDate date2 = date + date1;
	date2.show();
	cout << endl;
	
	date2 = 1+date;
	date2.show();
	cout << endl;
	
	return 0;
}

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

  • 39
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值