語法書 / AA 競程語法書 下冊 / 第十三單元 / 運算子多載:輸入與輸出(延伸知識)

13.7 運算子多載:輸入與輸出(延伸知識)

13.2 說過 cin >> s 是編譯錯誤——「沒人教過 cin 一個 student 長什麼樣」。現在你有工具教它了:>><< 也是運算子,一樣能多載。

不過這次不能寫成成員函式。13.6x < y,左邊是你的結構體,「自己」當之無愧;但 cin >> f左邊是 cin——型態是 istream,那是標準函式庫的東西,你塞不進去。所以這兩個多載要寫成普通函式,放在結構體外面:

#include <iostream>
using namespace std;

struct Fraction {
    long long p;    // 分子
    long long q;    // 分母
};

istream& operator >> (istream &is, Fraction &f) {   // 教 cin 怎麼讀一個 Fraction
    is >> f.p >> f.q;
    return is;                                      // 把 cin 還回去,>> 才能連著用
}

ostream& operator << (ostream &os, const Fraction &f) {   // 教 cout 怎麼印一個 Fraction
    os << f.p << '/' << f.q;
    return os;
}

int main() {
    Fraction a, b;
    cin >> a >> b;                     // 像讀 int 一樣讀兩個分數
    cout << a << ' ' << b << '\n';     // 像印 int 一樣印出來
    return 0;
}

執行結果(輸入 1 2 3 4):

1/2 3/4

逐項拆解:

  • 參數就是運算子的左右兩邊cin >> f 對應 operator >> (cin, f)——第一個參數是 stream(傳參考,因為讀取會改變 cin 的內部狀態)、第二個是你的結構體(operator >> 要把讀到的值寫進去,必須傳參考;operator << 只讀不寫,加 const)。
  • 回傳 stream 本人cin >> a >> b 是連鎖動作——cin >> a 先執行、回傳 cin,接力棒才交得到 >> b 手上。忘了寫 return is;,編譯器只會給個警告——但這是上冊 6.8 說的未定義行為:本機不開最佳化時可能碰巧看起來正常,開了最佳化(OJ 用的 -O2)實測直接當掉。stream 一定要還回去。
  • 附贈:因為回傳的是 stream,上冊 4.8 的 EOF 輸入模式 while (cin >> f) 對你的型態也自動成立。

多載真正的甜頭在於格式細節全部收進函式裡。有些題目的分數輸入長成 15/2 這種樣子——把斜線當一個字元讀掉就好,主程式照樣 cin >> a,一個字都不用改:

istream& operator >> (istream &is, Fraction &f) {
    char slash;                        // 用來接住中間的 '/'
    is >> f.p >> slash >> f.q;
    return is;
}

競程裡的定位:偶爾好用、不是必學。「一包資料常常整包讀寫」的場合(分數、座標、複數)最划算;只讀寫一兩次的話,成員函式或逐成員讀寫更省事。

動手試試看:解掉屋頂面積——輸入是 15/2 這種斜線格式的分數、輸出也要求最簡分數,正是本節斜線版 >> 加上 13.4 reduce() 的舞台。提示:屋頂面積怎麼用分數的加、減、乘拼出來,先畫圖想清楚;計算全程用分數,最後約分再輸出。