重载<<操作符
回头看我们之前的 rational.cpp,你满意了吗?反正我是觉得那些代码的可读性仍然欠佳:main 函数里边要多次调用 print 方法才能实现分数打印,酱紫不行!
如何通过重载 << 操作符来实现 print 打印分数的功能。( <<官方叫插入器 )
你或许知道,或许不知道,从第一次输出值开始,<< 操作符就一直被重载!
例如:std::cout << “Hello FishC!”;
C 标准库对左移操作符(<<)进行了重载,让它可以把值发送到一个流去(流的概念)。 但是在这个栗子中,iostream 库对新的 Rational 类表示一无所知,所以不能直接用 << 来输出我们的有理数(分数)。 但是,没有什么能够阻挡我们重载 << 操作符来让它接受 Rational 对象的宏伟愿望!
另外一个原因也比较重要:因为,重载的含义本身就是可以用相同的名字去实现不同的功能:输入参数方面有所差异就不会有问题。当然,我们无法在现有的 ostream 类里专门添加一个新的 operator <<()方法。所以我们只能定义一个正常的函数在外部重载这个操作符,这与重载方法的语法大同小异,唯一的区别是不再有一个对象可以用来调用 << 重载函数,而不得不通过第一个输入参数向这个重载方法传递对象。注意区别前边我们对四则运算符的重载。
下面是一个 operator <<()函数的原型:
1 | std::ostream& operator<<( std::ostream& os, Rational f ); |
- 第一个输入参数 os 是将要向它写数据的那个流,它是以“引用传递”方式传递的。
- 第二个输入参数是打算写到那个流里的数据值,不同的 operator <<()重载函数就是因为这个输入参数才相互区别的
- 返回类型是 ostream 流的引用。一般来说,在调用 operator <<()重载函数时传递给它的是哪一个流,它返回的就应该是那个流的一个引用。
好了,介绍就说这么多,我们对 Rational.cpp 进行改造吧:Rational2.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #include <iostream> #include <string> #include <stdlib.h> class Rational { public : Rational( int num, int denom); // num = 分子, denom = 分母 Rational operator+(Rational rhs); // rhs == right hand side Rational operator-(Rational rhs); Rational operator*(Rational rhs); Rational operator/(Rational rhs); private : void normalize(); // 负责对分数的简化处理 int numerator; // 分子 int denominator; // 分母 friend std::ostream& operator<<(std::ostream& os, Rational f); }; Rational::Rational( int num, int denom) { numerator = num; denominator = denom; normalize(); } // normalize() 对分数进行简化操作包括: // 1. 只允许分子为负数,如果分母为负数则把负数挪到分子部分,如 1/-2 == -1/2 // 2. 利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5 void Rational::normalize() { // 确保分母为正 if ( denominator < 0 ) { numerator = -numerator; denominator = -denominator; } // 欧几里德算法 int a = abs (numerator); int b = abs (denominator); // 求出最大公约数 while ( b > 0 ) { int t = a % b; a = b; b = t; } // 分子、分母分别除以最大公约数得到最简化分数 numerator /= a; denominator /= a; } // a c a*d c*b a*d + c*b // - + - = --- + --- = --------- // b d b*d b*d = b*d Rational Rational::operator+(Rational rhs) { int a = numerator; int b = denominator; int c = rhs.numerator; int d = rhs.denominator; int e = a*b + c*d; int f = b*d; return Rational(e, f); } // a c a -c // - - - = - + -- // b d b d Rational Rational::operator-(Rational rhs) { rhs.numerator = -rhs.numerator; return operator+(rhs); } // a c a*c // - * - = --- // b d b*d Rational Rational::operator*(Rational rhs) { int a = numerator; int b = denominator; int c = rhs.numerator; int d = rhs.denominator; int e = a*c; int f = b*d; return Rational(e, f); } // a c a d // - / - = - * - // b d b c Rational Rational::operator/(Rational rhs) { int t = rhs.numerator; rhs.numerator = rhs.denominator; rhs.denominator = t; return operator*(rhs); } std::ostream& operator<<(std::ostream& os, Rational f); //函数声明 int main() { Rational f1(2, 16); Rational f2(7, 8); // 测试有理数加法运算 std::cout << f1 << " + " << f2 << " == " << (f1+f2) << "\n" ; // 测试有理数减法运算 std::cout << f1 << " - " << f2 << " == " << (f1-f2) << "\n" ; // 测试有理数乘法运算 std::cout << f1 << " * " << f2 << " == " << (f1*f2) << "\n" ; // 测试有理数除法运算 std::cout << f1 << " / " << f2 << " == " << (f1/f2) << "\n" ; return 0; } std::ostream& operator<<(std::ostream& os, Rational f) { os << f.numerator << "/" << f.denominator; return os; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步