通訊錄整包讀寫(延伸) 題解
簡潔題意
照規格定義結構體 student(三個成員),並在結構體外面多載 >>(讀一位同學)與 <<(印一位同學、不換行)。評測程式用 while (cin >> s) 讀到 EOF,再倒序 cout << v[i] << '\n'。
解法
這是 14.8 運算子多載:輸入與輸出 的延伸題——14.6 的 operator < 寫成成員函式,因為 x < y 的左邊是自己的結構體;但 cin >> s 的左邊是 cin,型態是 istream,那是標準函式庫的東西,你塞不進去。所以 >>、<< 要寫成結構體外面的普通函式,參數就是運算子的左右兩邊:
istream& operator >> (istream &is, student &s) { // cin >> s 就是 operator >> (cin, s)
is >> s.name >> s.seat >> s.phone;
return is;
}
四個細節,每一個評測程式都會檢查:
is傳參考:讀取會改變 stream 的內部狀態(讀到哪了、有沒有失敗)。s傳參考、不加const:>>要把讀到的值寫進s——傳值的話寫進去的是複本,呼叫端的s什麼都拿不到。<<只讀不改,所以那邊寫const student &s。- 回傳 stream 本人:
return is;。評測程式的while (cin >> s)靠回傳的 stream 判斷「上一次讀成功了沒」,讀到 EOF 時 stream 變成失敗狀態、迴圈才停得下來;cout << v[i] << '\n'也是靠<<回傳的cout接力印出換行。 <<不換行:規格說換行由呼叫端補,你的<<只印座號 姓名 電話——這也是 14.8 範例的寫法,印完之後要不要換行、要不要接著印別的,交給呼叫的人決定。
順帶一提,這題的輸入沒有行數,正是上冊 4.8 的 EOF 輸入模式——多載好 >>,while (cin >> s) 對你的型態就自動成立。
常犯錯誤
- 把
>>寫成成員函式istream& operator >> (istream &is)(CE):[檢查] 還沒有多載 operator >>:要寫在結構體外面 … istream& operator >> (istream &is, student s)——s傳值(CE):[檢查] operator >> 的第二個參數要是 `student &s`void operator >> (…)——沒回傳 stream(CE):[檢查] operator >> 必須回傳 istream&ostream& operator << (ostream &os, student &s)——少了const(CE):[檢查] operator << 的第二個參數要是 const student &void operator << (…)(CE):[檢查] operator << 必須回傳 ostream&<<裡自己印了'\n'(WA):每兩筆之間多一個空行- 回傳型態寫對了,但函式最後忘了
return is;(編譯只給警告):這是未定義行為,評測程式擋不到;在 OJ 的-O2下多半直接當掉(RE)或迴圈停不下來
最後一種最陰險:本機不開最佳化時可能碰巧看起來正常。寫 >>/<< 的固定動作——最後一行永遠是 return is;/return os;。
參考程式碼
struct student {
string name; // 姓名
int seat; // 座號
string phone; // 電話:可能以 0 開頭,所以是字串不是整數
};
istream& operator >> (istream &is, student &s) { // 教 cin 怎麼讀一位同學:s 要傳參考,讀到的值才回得去
is >> s.name >> s.seat >> s.phone;
return is; // 把 stream 還回去,while (cin >> s) 才知道何時該停
}
ostream& operator << (ostream &os, const student &s) { // 教 cout 怎麼印一位同學:只讀不改,加 const
os << s.seat << ' ' << s.name << ' ' << s.phone; // 不換行,換行由呼叫端決定
return os;
}