c++编程(6)——类与对象(4)运算符重载、赋值重载函数

本文介绍了C++中的运算符重载,包括赋值重载函数的实现,以及如何重载比较运算符如==、>、>=、<等,强调了运算符重载函数需遵循运算符本身的逻辑以保持代码可读性和正确性。
摘要由CSDN通过智能技术生成

欢迎来到博主的专栏——C++编程
博主ID:代码小豪

运算符重载

重载这个概念在c++中已经出现两次了,在前面的文章中,函数重载指的是可以用相同名字的函数实现不同的功能。而运算符重载则是可以用相同的运算符实现不同的功能。

运算符也能重载吗?在C语言也有可以复用的运算符,在不同的运用场景下有不同的作用,比如乘法操作符(*)和解引用操作符(*),取地址操作符(&)和位运算操作符(&)。

但这些和c++的运算符重载可不一样。在c++语言中,可以用operator加上运算符来表示函数,叫做运算符重载。运算符重载的声明格式如下:

返回类型 operator 运算符(参数列表……);

运算符是一种特殊形式的函数,即将运算符重载成一个与原运算符符号一致,但是重载运算符是一个函数,而原操作符是一个操作符,这个操作符就成为了一个可以调用的函数名。

其实大家在初学c++的时候就已经见过重载的运算符了。

	cout << “hello world” << endl;

这个和左移操作符一样的就是重载操作符,其函数原型可以在c++参考手册中可以看到。

c++参考手册网址:cplusplus

ostream& operator<< (bool val);ostream& operator<< (short val);ostream& operator<< (unsigned short val);ostream& operator<< (int val);ostream& operator<< (unsigned int val);ostream& operator<< (long val);ostream& operator<< (unsigned long val);ostream& operator<< (float val);ostream& operator<< (double val);ostream& operator<< (long double val);ostream& operator<< (void* val);
	
ostream& operator<< (streambuf* sb );
	
ostream& operator<< (ostream& (*pf)(ostream&));ostream& operator<< (ios& (*pf)(ios&));ostream& operator<< (ios_base& (*pf)(ios_base&));

赋值重载函数

我们还是再拿date类型举例:

class Date
{
public:
	Date(int year = 2024, int month = 4, int day = 16);//默认构造函数
	Date(const Date& d);//拷贝构造函数

	Date& operator =(const Date& D1);//赋值重载函数
private:
	int _year;
	int _month;
	int _day;
};
Date::Date(int year, int month, int day)//默认构造函数
{
	_year = year;
	_month = month;
	_day = day;
}

Date::Date(const Date& d)//拷贝构造函数
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

内置类型的赋值操作符是(=)。赋值重载函数可以让自定义类型的变量用(=)实现赋值操作。首先要让赋值操作符重载成函数。Date类的复制操作函数的声明如下:

Date& operator =(const Date& D1);//赋值重载函数


既然函数是要实现赋值操作,那么函数的定义内容就是要让Date类的变量进行拷贝。

Date& Date::operator=(const Date& d1)
{
	if (this != &d1)//若是参数是对象本身,那么就不需要将成员一一拷贝,无意义且浪费时间
	{
		_year = d1._year;
		_month = d1._month;
		_day = d1._day;
	}
	return (*this);
}

参数类型使用const Date&,传递引用可以提高效率
返回类型使用const Date&,提高效率
检测是否为自己赋值,提高效率
返回*this可以让对象实现连续赋值

调用复制重载函数有两种方式,一种是和函数一样传递参数。

	Date d1;
	Date d2;
	Date d3;
	d1.operator=(d2);
	d1.operator=(d2).operator=(d3);
	

也可以用运算符的形式调用函数,运算符与调用时的不同之处在于:对于普通函数,实参出现在函数的括号内,而操作符调用,实参出现在操作符两侧:

	d1 = d2;
	d1 = d2 = d3;

当运算符重载在类中时,函数显示的调用实参只有一个,但是实际上参与函数的实参有两个,一个是指向对象的this指针(this指针在上一篇文章中讲过),另外一个是被显示调用的实参。

在上例中,调用函数的对象是d1,因此this指针指向d1,将this解引用(*this)后得到的是d1,实参传递的是d2.

重载的赋值运算符是一个二元操作符,因此实际调用的参数必须是两个,比如将复制重载函数的声明修改成下例,那么编译器会报错。

Date& operator =(const Date& D1,Date&D2);

在这里插入图片描述
这是因为这个赋值重载函数是声明在类中的,类的this指针占据的一个参数,D1和D2作为实际上传的参数有两个,那么这个赋值重载函数的的参数共计三个。赋值操作符(=)如果是一个三元操作符,那么肯定是不符合逻辑的,因而不能超过操作符的操作数。

定义在全局(类外)的赋值重载函数的参数列表就可以声明两个形式参数。

Date& Date::operator =(Date& D1, Date& d2);//赋值重载函数

定义在类外的函数没有this指针指向对象,因此可以定义两个参数。

默认赋值重载函数

我们将Date类的赋值重载函数的声明和定义注释掉,运行以下代码:

    Date d1;
	Date d2;
	Date d3;
	d1 = d2;
	d1 = d2 = d3;

通过调试,可以发现这段代码的编译过了,而且也实现的拷贝的功能
在这里插入图片描述
明明没有定义复制重载函数,怎么还能实现这些操作呢?这是因为若是用户没有实现赋值重载函数,编译器会生成一个默认的赋值重载函数,这个默认赋值重载函数是以值拷贝的形式实现的。和默认拷贝构造函数的作用类似。内置类型的成员变量直接赋值,自定义类型的成员变量则调用该类的赋值重载函数。

其他运算符重载函数

其他的运算符重载函数就没有赋值运算符重载那么重要了(毕竟赋值运算符有默认的函数生成,而其他的没有,除了取地址重载(&))。不能重载的运算符有(.*)(::)(sizeof)(.)(?:)。

具体要重载什么运算符,需要看这个类要实现什么功能,以Date类为例,我们可以实现判断的运算符(==,>,<等)。

    bool operator==(const Date& D1)const;
    
    bool operator>(const Date& D1)const;

	bool operator!=(const Date& D1)const;

	bool operator>=(const Date& D1)const;
	
	bool operator<(const Date& D1)const;

在类中声明以上函数。

(==)需要判断两个对象是否相同,我们思考一下日期如何才算是相同?相同的日期需要具有以下特点:
年相等
月相等
日相等。

根据这个逻辑得到下面的函数定义:

bool Date::operator==(const Date& d1)const
{
	return _year == d1._year &&
		_month == d1._month &&
		_day == d1._day;
}

运算符重载函数并不需要将所有的内容都实现出来,有时候可以复用其他的重载函数,前提是符合这个运算符的逻辑

例如不等(!=)运算符,其逻辑是与(==)相反的,因此可以复用==运算符重载函数实现。

bool Date::operator!=(const Date& D1)const
{
	return !(*this == D1);
}

\(>)大于重载运算符的逻辑应该如下:
比较的日期的年份,大于则返回ture。
若年份相等,比较月份,大于则返回true
若月份相等,比较天数,大于则返回true
其余情况(包括日期相等)都返回false。

bool Date::operator>(const Date& D1)const
{
	if (_year > D1._year)
	{
		return true;
	}
	else if (_year == D1._year)
	{
		if (_month > D1._month)
		{
			return true;
		}
		else if (_month == D1._month)
		{
			if (_day > D1._day)
			{
				return true;
			}
		}
	}

	return false;
}

接着实现>=和<,这些运算符的逻辑都是能和>和==运算符复用的。实现如下:

bool Date::operator>=(const Date& D1)const
{
	return (*this) == D1 || (*this) > D1;
}

bool Date::operator<(const Date& D1)const
{
	return !(*this >= D1);
}

为什么运算符重载函数需要符合运算符的逻辑呢?大家可以试一下,比如讲大于的逻辑写成小于,或者其他功能。这个函数是能通过编译的,但是这么做有什么好处呢?当然是什么都没有。我们可以涉想这么一个场景。你是某个公司的程序员,你从别人那里调用了一个库,这个库将某个类的加法运算符的逻辑写成了减法。并且导致了你的程序不停的出错花费了大量时间调试和修改,最后发现是同事的恶作剧,那么请问你什么感受呢?因此运算符重载函数的逻辑应该与运算符的本身逻辑一致。大大的增加了程序的可读性。这样才是c++推出运算符重载的初衷。

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码小豪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值