//C++学习笔记_08 运算符重载
#include<cstdio>
#include<iostream>
using namespace std;
//有理数类
class CRationalNum
{
private:
int bad; //分母为0,这是一个无效的数 bad=1 表示数字无效
int num; //分子
int den; //分母
public:
CRationalNum() :num(0), den(1),bad(0){} //默认值是 0
CRationalNum(int x, int y=1) :num(x), den(y), bad(0) //int y=1:C++里面可以定义参数的默认值
{ reduct();}
//约分的函数(找最大公约数--辗转相除法)
void reduct(){
if (den == 0)//分母为0,这个数无效
{ bad = 1;return; }
if (num == 0)//值必然为0
{ den = 1; return;}
if (den < 0) //分母为负数,把符号给分子
{ num = -num;den = -den; }
int d_num = num;// d_num 取分子的绝对值
if (num < 0){ d_num = -num;}
int x = d_num > den ? d_num : den; //x 取两个里面大的
int y = d_num > den ? den : d_num; //y 取两个里面小的
//= d_num + den - x
int c = x % y;
while (c)
{ x = y;y = c;c = x % y; }
//c == 0 时,得到最大公约数 y
num /= y;den /= y;return;
}
//输入的时候, rNum 会被修改,不能再使用 const
friend istream& operator >> (istream &is, CRationalNum &rNum)
{
is >> rNum.num >> rNum.den;
rNum.reduct();
return is;
}
//重载输出运算符 <<
//1:友元
//2:返回值 是 ostream
//3:入参加上 ostream
//4:入参是 ostream 引用
//5:返回值是引用类型
friend ostream& operator << (ostream &os, const CRationalNum &rNum)
{
//这里面就没有了 num, 和 den,也没有 this 指针
//没有this指针,也就是说,这个函数不是 CRationalNum 的成员函数
//而是一个外部函数(或者说是 cout 对象的成员函数)
//外部函数,要访问自己的私有成员 ---》需要函数声明成 friend
//cout 是 ostream 的对象,这里要体现的话,只能是入参
//返回值:cout << a << b ==> 可以看作 xxx = cout << a; xxx << b;
// 这里连续输出,应该返回 ostream 对象
if (rNum.bad == 1) { os << "NaN";}
else if (rNum.den == 1) { cout << rNum.num;}
else { cout << rNum.num << "/" << rNum.den;}
return os; //返回一个局部变量,不允许
// --> 把入参声明从引用
//ostream 的对象,不允许复制,
// 作为返回值,有可能赋值给别人,不允许
// --》返回值类型,定义成引用
}
//重载 + 运算符 //operator 声明 + 是一个运算符函数
//运算符重载 xxx,就是在本来应该写函数名的地方,写上 operator xxx
CRationalNum operator + (const CRationalNum &rNum) const
{
//括号里面的const 表示 rNum 是常量,不允许函数内部修改 rNum 的值
//外面的const 修饰的是函数,表示函数内不能够修改 成员变量
if (this->bad == 1) { return *this;}//bad 表示这个数非法
else if (rNum.bad == 1) { return rNum;}
else{
// a/b + c/d = (a*d+c*b)/b*d
int x; //保存结果分子部分
int y; //保存结果分母部分
x = num * rNum.den + rNum.num * den;
y = den * rNum.den;
return CRationalNum(x, y);
}
}
void Print(){
if (bad == 1) { cout << "NaN" << endl; }
else if (den == 1) { cout << num << endl; }
else { cout << num << "/" << den << endl; }
return;
}
//1: 分数的运算: 加减乘除
//2: 分数的输入输出:
//3: 约分
CRationalNum add(const CRationalNum &rNum) const
{
if (this->bad == 1) { return *this;}//bad 表示这个数非法
else if (rNum.bad == 1){ return rNum;}
else{
// a/b + c/d = (a*d+c*b)/b*d
int x; //保存结果分子部分
int y; //保存结果分母部分
x = num * rNum.den + rNum.num * den;
y = den * rNum.den;
return CRationalNum(x, y);
}
}
/*
//4: 分母为 0 怎么处理? --》就是一个无穷大(无穷小[分子为负数])的数
// 4-0: 分子为 0,则无论分母是多少,都认为这个数都是 0
// 4-1: 无穷大(小)数+正常的数 = 无穷大(小)数
// 4-2: 无穷大(小)数 + 无穷大(小)数 = 无穷大(小)数
// 4-3: 无穷大数+无穷小数 = 0
//方案1:定义函数做加减乘除运算
CRationalNum add(const CRationalNum &rNum) const
{
//两个有理数相加:
// 但是,我们这里只看到了 1 个有理数 rNum,还一个在哪里?
// 还有一个对象,就是调用 add 成员函数的这个对象,*this
// rNum1.add(rNum2)
//注意:
// 1: den 是 rNum 的私有成员,这里 可以通过 rNum.den 来访问
// 因为:只要是在类的成员函数里面,就允许直接使用
// 2: 谁调用了 add 函数,那么函数里面的 this 变量,就指向谁
// this 指向 CRationNum 对象的指针
// 比如 rNum1.add(rNum2)
// --> this 就是 &rNum1; *this 就是 rNum1
//一般情况下 this->num, this->den 可以直接写为 num, den
//在入参和 这个变量重名的情况下,我们再使用 this 来进行区分
if (den == 0 && rNum.den == 0) //就是 *this.den (this->den)
{
//两个无穷数
if (num * rNum.num > 0){ return rNum;} //同为无穷大或者同为无穷小
else //一个无穷大一个无穷小
{return CRationalNum();}//类名后加一个括号,表示生成一个匿名对象
}
else if (den == 0) { return *this;}
else if (rNum.den == 0) { return rNum; }
else{
// a/b + c/d = (a*d+c*b)/b*d
int x; //保存结果分子部分
int y; //保存结果分母部分
x = num * rNum.den + rNum.num * den;
y = den * rNum.den;
return CRationalNum(x, y);
}
}
*/
};
int main()
{
CRationalNum rNum1;
CRationalNum rNum2(42,111);
CRationalNum rNum3(42, 0);
rNum1.Print();
rNum2.Print();
rNum3.Print();
CRationalNum rNum4(-1, 2);
CRationalNum rNum5(1, 6);
rNum1 = rNum4.add(rNum5);
rNum1.Print();
//1: 对有理数做加减乘除运算,能不能 直接使用 rNum1 = rNum4 + rNum5 ?
//2: 输出的时候,能不能直接使用 cout << rNum1 << endl;
//--> 运算符重载 :使用 operator + 替代 函数名 add
rNum1 = rNum4 + rNum5; //这里 rNum4 调用了 + 号运算
//入参是 rNum5
rNum1.Print();
rNum1 = CRationalNum(1,2) + CRationalNum(1,3) + CRationalNum(1, 4);
cout << "\nrNum1:";
rNum1.Print();
//关于输出:前面有东西还得分开写,
// Print() 自带换行,如果我们想一行输出两个分数,没办法
//我们能不能 简单点 使用 cout << rNum 进行输出?
//--> 重载输出运算符
//cout << rNum1; // cout 这个对象, 调用了 << 运算符,入参是 rNum1
//这里,不再是 rNum1 调用某个函数
//所以,这个运算符重载 rNum1 是入参
rNum1 = rNum4 + rNum5;
cout << rNum4 << " + " << rNum5 << " = " << rNum1 << endl;
//注意:上述语句,调用了 3 次 << 运算符重载
cout << "输入有理数:";
cin >> rNum1;
cout << "rNum:" << rNum1 << endl;
return 0;
}
//作业:
//1: 实现 -, *, / 运算符重载
//2: 实现 >> 输入运算符重载
//3: 比较运算重载 >, >=, <, <=, ==, !=
//4: ++, --, +N , -N, +=,-= 重载
// ++,-- 涉及到前加加,后加加