背景题目:
定义有理数类(分母不为0的分数,分子分母均为整数)Rational,实现相应操作符的重
载。
(1)定义私有数据成员:分子int iUp; 分母 int iDown。
(2)定义私有成员函数:void Reduce() 和 int Gcd(int l, int r),分别用于有理数的
约简和求两个整数的最大公约数。其中,在约简时需要求取分子与分母的最大公约数。
(3)定义构造函数,在构造函数体内可调用Reduce对有理数进行约简。
(4)将负号-和赋值运算符=重载为公有成员函数,分别用于求有理数的负数和赋值。
(5)将前置++、前置–、后置++、后置–重载为公有成员函数,实现有理数自增1或自减
1。
(6)将+、-、*、/重载为友员函数,实现有理数的加减乘除。
(7)将<、<=、>、>=重载为友员函数,实现有理数的大小关系比较。
(8)重载流插入符<<和流提取符>>,分别用于有理数的输出和输入。其中,输出格式为
“分子/分母”,若为整数,则直接输出整数。
为便于说明,此处只给出部分代码:
#include<iostream>using namespace std;class Rational
{
private: int iUp;int iDown;void Reduce(){...}int Gcd(int l, int r){...}public:Rational(){}Rational(int u, int d){...}Rational& operator=(Rational& r){iUp=r.iUp;iDown = r.iDown;Reduce();return *this;}Rational operator-(){Rational temp;temp.iUp = -iUp;temp.iDown = iDown;return temp;}Rational& operator++(){iUp += iDown;Reduce();return *this;}Rational operator++(int){Rational temp = *this;iUp += iDown;Reduce();return temp;}Rational& operator--(){iUp -= iDown;Reduce();return *this;}Rational operator--(int){Rational temp = *this;iUp -= iDown;Reduce();return temp;}friend Rational operator+(const Rational& a, const Rational& b);friend Rational operator-(const Rational& a, const Rational& b);friend Rational operator*(const Rational& a, const Rational& b);friend Rational operator/(const Rational& a, const Rational& b);friend bool operator<(const Rational& a, const Rational& b);friend bool operator<=(const Rational& a, const Rational& b);friend bool operator>(const Rational& a, const Rational& b);friend bool operator>=(const Rational& a, const Rational& b);friend istream& operator>>(istream& in, Rational& r);friend ostream& operator<<(ostream& out, const Rational& r);
};Rational operator+(const Rational& a, const Rational& b)
{Rational temp;temp.iUp = a.iUp * b.iDown + b.iUp * a.iDown;temp.iDown = a.iDown * b.iDown;temp.Reduce();return temp;
}
Rational operator-(const Rational& a, const Rational& b)
{Rational temp;temp.iUp = a.iUp * b.iDown - b.iUp * a.iDown;temp.iDown = a.iDown * b.iDown;temp.Reduce();return temp;
}
Rational operator*(const Rational& a, const Rational& b)
{Rational temp;temp.iUp = a.iUp * b.iUp;temp.iDown = a.iDown * b.iDown;temp.Reduce();return temp;
}
Rational operator/(const Rational& a, const Rational& b)
{Rational temp;temp.iUp = a.iUp * b.iDown;temp.iDown = a.iDown * b.iUp;temp.Reduce();return temp;
}bool operator<(const Rational& a, const Rational& b)
{...
}
bool operator<=(const Rational& a, const Rational& b)
{...
}
bool operator>(const Rational& a, const Rational& b)
{...
}
bool operator>=(const Rational& a, const Rational& b)
{...
}istream& operator>>(istream& in, Rational& r)
{...
}
ostream& operator<<(ostream& out, const Rational& r)
{...
}int main()
{int up, down;cin >> up >> down;Rational a(up, down);cin >> up >> down;Rational b(up, down);Rational c;c = a + b;cout << "a+b: " << c;c = a - b;cout << "a-b: " << c;c = a * b;cout << "a*b: " << c;c = a / b;cout << "a/b: " << c;c = -a;cout << "-a: " << c;c = ++a;cout << "++a: " << c;c = --a;cout << "--a: " << c;c = a++;cout << "a++: " << c;c = a--;cout << "a--: " << c;...return 0;
}
欲运行代码,结果报错:
可以看到等号与+、-、*、/、后置自增、后置自减连用的地方,都标红,报错信息为“error C2679: 二进制“=”: 没有找到接受’Rational’类型的右操作数的运算符(或没有可接受的转换)”。
可是赋值运算符已经重载了,为什么这里会没有起作用呢?经过查询,我发现,问题出在重载等号传入的参数和其他运算符的返回值类型上。
源码中重载等号的参数类型是Rational&,是一个可变引用
Rational& operator=(Rational& r){iUp=r.iUp;iDown = r.iDown;Reduce();return *this;}
重载+、-、*、/、后置自增、后置自减的返回参数均为Rational,是一个临时局部变量。
Rational operator+(const Rational& a, const Rational& b)
{Rational temp;temp.iUp = a.iUp * b.iDown + b.iUp * a.iDown;temp.iDown = a.iDown * b.iDown;temp.Reduce();return temp;
在C++里,临时局部变量没有合法内存空间,是不可以转为可变引用的,因为可变引用需要有合法的内存空间。但是常量引用可以接收这一类没有有效内存空间的值,即const Rational&。
于是我将重载等号代码修改,
Rational& operator=(const Rational& r){iUp=r.iUp;iDown = r.iDown;Reduce();return *this;}
问题就解决了。
接下来讲一下常量引用的原理,以整型常量引用为例:
int &a=10;//会报错,10没有申请合法内存空间
const int &a=10;//不会报错//编译器会自动将代码优化为int temp=10;//const int &a=temp;
总结:
当不涉及修改数据操作时,传递参数最好用常量引用的形式,即const type&,因为这个方式是适应性最高的,可以接收有合法内存空间的变量或常量,也可以接收没有事先申请合法内存空间的临时(局部)变量或常量,还可以保护数据,防止误操作。