C++知识点39——运算符的重载概念与分数类实现(上)

一、概念

在博客https://blog.csdn.net/Master_Cui/article/details/109461561已经提到了赋值运算符的重载,重载运算符是有特殊名字的函数:名字由operator后接运算符组成。可以有返回值和形参。除了重载函数调用运算符operator()外,其他重载运算符函数不能有默认实参。重载运算符函数是用来完成自定义类的运算符操作,不能重载内置类型的运算符,不能新发明运算符进行重载。

已有的运算符中,大部分能重载,小部分不能重载,如下图

在上述能重载的运算符中,有些不应该被重载:||、&&、,以及&。||和&&被重载会无法保留内置的||和&&的短路逻辑,而,以及&不应该被重载是因为其对于内置类型的含义与自定义类型的含义一样。

通过上述四个不应该被重载的运算符可以知道:类的重载运算符的原则就是和内置类型的运算符的含义保持一致。如果重载IO操作,那么<<和>>应该和内置类型保持一致;如果定义了operator==,那么也应该定义operator!=;如果定义了operator<,那么也应该定义其他的关系操作;重载运算符的返回值类型应该和内置版本的运算符的返回值类型保持一致,逻辑运算符和关系运算符应返回bool;算数运算符应该类类型的值;而赋值运算符和复合赋值运算符(+=...)应该返回左侧对象的引用

重载运算符可以被声明为成员函数,也可以被声明为非成员函数。有些运算符必须是成员函数,有些作为非成员函数比较好:

1、=,[],(),->这四个运算符必须是成员函数;

2、复合赋值运算符、++、--、*(解引用)应该得是成员的;

3、算数运算符、相等运算符、关系运算符和位运算符通常应该是非成员的

4、输入输出运算符必须是非成员的

在博客https://blog.csdn.net/Master_Cui/article/details/106363667中说过,string的operator+是非成员函数,因此可以进行如下的初始化操作

string s="hello";
string s1=s+"!";
string s2="hi" + s1;

如果operator+是成员函数,那么第一个加法等价于s.operator+("!");第二个加法等价于"hi".operator+(s1);但是"hi"是个字面值(内置类型),根本没有operator+,所以,如果将operator+设置为成员函数,那么类的某些操作就无法通过编译,扩展性就会变差

而因为operator+不是成员函数,所以第一个加法等价于operator+(s, "!");第二个加法等价于operator+("hi", s1);可以编译通过并返回一个新的string对象的拷贝

 

二、重载运算符实战

通过实现一个分数类fraction来应用常用的运算符重载,分数的结果需要化为最简,最大公约数的算法见博客https://blog.csdn.net/qq_31828515/article/details/51812154

1.fraction类的整体结构如下:

#include <iostream>
#include <vector>
#include <iterator>
#include <utility>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <memory>

using namespace std;

class fraction
{
friend ostream &operator<<(ostream &os, const fraction &data);//>>和<<重载
friend istream &operator>>(istream &is,  fraction &data);

friend fraction operator+(const fraction &one, const fraction &two);//算术运算符重载
friend fraction operator-(const fraction &one, const fraction &two);
friend fraction operator*(const fraction &one, const fraction &two);
friend fraction operator/(const fraction &one, const fraction &two);

friend bool operator==(const fraction &one, const fraction &two);//关系运算符重载
friend bool operator!=(const fraction &one, const fraction &two);
friend bool operator>(const fraction &one, const fraction &two);
friend bool operator<(const fraction &one, const fraction &two);
friend bool operator>=(const fraction &one, const fraction &two);
friend bool operator<=(const fraction &one, const fraction &two);

public:
	fraction(int numerator=0, int denominator=1);
	fraction(const fraction &t);

	fraction &operator=(const fraction &t);
	fraction &operator+=(const fraction &t);
	fraction &operator-=(const fraction &t);
	fraction &operator*=(const fraction &t);
	fraction &operator/=(const fraction &t);

	int getmaxcommondivisor(const fraction &t) const;
	bool isneedtoaddnegtive(const fraction &t) const;

	fraction &operator++();//前置++
	fraction &operator--();
	fraction operator++(int unused);//后置++
	fraction operator--(int unused);

	fraction operator()(const fraction &para);//函数调用运算符

	explicit operator bool() const;//类型转换运算
	

	~fraction() {cout<<__func__<<endl;}

private:
	int numerator_;
	int denominator_;
};

ostream &operator<<(ostream &os, const fraction &data);//>>和<<重载
istream &operator>>(istream &is,  fraction &data);

fraction operator+(const fraction &one, const fraction &two);//算术运算符重载
fraction operator-(const fraction &one, const fraction &two);
fraction operator*(const fraction &one, const fraction &two);
fraction operator/(const fraction &one, const fraction &two);

bool operator==(const fraction &one, const fraction &two);//关系运算符重载
bool operator!=(const fraction &one, const fraction &two);
bool operator>(const fraction &one, const fraction &two);
bool operator<(const fraction &one, const fraction &two);
bool operator>=(const fraction &one, const fraction &two);
bool operator<=(const fraction &one, const fraction &two);

其中计算最大公约数和判断是否需要加负号的函数实现如下

int fraction::getmaxcommondivisor(const fraction &t) const
{
	unsigned absnumerator=abs(t.numerator_);
	unsigned absdenominator=abs(t.denominator_);
	while (absnumerator!=absdenominator) {
		if (absnumerator>absdenominator) {
			absnumerator-=absdenominator;
		}
		else {
			absdenominator-=absnumerator;
		}
	}

	return absnumerator;
}

bool fraction::isneedtoaddnegtive(const fraction &t) const
{
	bool res1=t.numerator_>0 && t.denominator_>0;
	bool res2=t.numerator_<0 && t.denominator_<0;
	if (res1 || res2) {
		return false;
	}
	return true;
}

2.重载输入(>>)输出(<<)运算符

重载输入运算符的第一个参数是>>要读取的流的非const引用(因为无法对流对象进行拷贝操作,所以要传引用,而输入操作会改变流的状态,所以是非const的),第二个参数是要接受流中数据的非常量对象的引用(因为接受了流中的数据,complex对象改变了,不能是const)

istream &operator>>(istream &is, fraction &data)
{
	cout<<__func__<<endl;
	is>>data.numerator_>>data.denominator_;
	return is;
}

重载输出运算符的第一个参数是<<要写入的流的非const引用(因为无法对流对象进行拷贝操作,所以要传引用,而写入操作会改变流的状态,所以是非const的),第二个参数是要写入流中数据的常量对象的引用(引用可以避免拷贝,而加const是因为输出操作不会改变对象的状态)

ostream &operator<<(ostream &os, const fraction &data)
{
	cout<<__func__<<endl;
	bool negflag=data.isneedtoaddnegtive(data);
	int maxcommondivisor=data.getmaxcommondivisor(data);
	if (negflag) {
		os<<"-"<<abs(data.numerator_/maxcommondivisor)<<"/"<<abs(data.denominator_/maxcommondivisor);
	}
	else {
		os<<abs(data.numerator_/maxcommondivisor)<<"/"<<abs(data.denominator_/maxcommondivisor);
	}
	//输入操作本身不要添加格式操作(比如换行,缓冲刷新等),只负责打印即可。由用户决定是否添加格式操作
	return os;
}

测试

int main(int argc, char const *argv[])
{
   	fraction t;
	cin>>t;
	cout<<t<<endl;
	return 0;
}

此外,输入输出运算符必须是非成员函数。这是因为iostream中的输出输出不是成员运算符,可以输入输出各种类型,所以complex的重载的输入输出运算符也得是非成员函数

下篇文章见https://blog.csdn.net/Master_Cui/article/details/109515571

 

参考

《C++ Primer》

https://blog.csdn.net/qq_31828515/article/details/51812154

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值