(Effective C++學習筆記)條款 2:儘量用<iostream>而不用<stdio.h>

2020-10-25 12:00:56

條款 2:儘量用<iostream>而不用<stdio.h>

  • scanf printf 很輕巧,很高效,事實上 scanf printf 及其系列還可以做些改進,他們不是型別安全的,而且沒有擴充套件性。因為型別安全和擴充套件性是 C++基石,所以也要服從這一點。
  • scanf/printf 系列函數把要讀寫的變數和控制讀寫格式的資訊分開來,就象古老的 FORTRAN 那樣,scanf/printf 的這些弱點正是操作符>><<的強項:
int i;
Rational r; // r 是個有理數
...
cin >> i >> r;
cout << i << r;
  • 上面的程式碼要通過編譯,>><<必須是可以處理 Rational 型別物件的過載函數(可能要通過隱式型別轉換)
  • 如果沒有實現這樣的函數,就會出錯(處理 int不用這樣做,因為它是標準用法)
  • 編譯器自己可以根據不同的變數型別選擇操作符的不同形式,所以不必勞你去指定第一個要讀寫的物件是 int 而第二個是 Rational
  • 在傳遞讀和寫的物件時採用的語法形式相同,所以不必象 scanf 樣死記一些規定,比如如果沒有得到指標,必須加上地址符,而如果已經得到了指標,又要確定不要加上地址符。這些完全可以交給 C++編譯器去做。編譯器沒別的什麼事好做的,而你卻不一樣。最後要注意的是,象 int 這樣的固定類型和象 Rational這樣的自定義型別在讀寫時方式是一樣的。
  • 所寫的表示有理數的類的程式碼可能象下面這樣:
class Rational {
public:
	Rational(int numerator = 0, int denominator = 1);
	...
private:
	int n, d; // 分子,分母
	friend ostream& operator<<(ostream& s, const Rational& r);
};
ostream& operator<<(ostream& s, const Rational& r)
{
	s << r.n << '/' << r.d;
	return s;
}
  • 上面的程式碼涉及到 operator<<的一些微妙(但很重要)的用法
    • 例如:上面的 operator<<不是成員函數(條款 19 解釋了為什),而且,傳遞給 operator<<的不是 Rational 物件,而是定義為 const 的物件的參照(參見條款 22)operator>>的宣告和實現也類似。
  • 有些情況下回到那些經過證明而且正確的老路上去還是很有意義的
    • 第一,有些 iostream 的操作實現起來比相應的 C stream效率要低,所以不同的選擇會給你的程式有可能(雖然不一定,參見條款 M16)帶來很大的不同。但請牢記,這不是對所有的 iostream 而言,只是一些特殊的實現;參見條款 M23
    • 第二,在標準化的過程中,iostream 庫在底層做了很多修改(參見條款 49),所以對那些要求最大可移植性的應用程式來說,會發現不同的廠商遵循標準的程度也不同。
    • 第三,iostream 庫的類有建構函式而裡的函數沒有,在某些涉及到靜態物件初始化順序的時候,如果可以確認不會帶來隱患,用標準 C 庫會更簡單實用。
  • iostream 庫的類和函數所提供的型別安全和可延伸性的價值遠遠超過你當初的想象,所以不要僅僅因為用慣了而捨棄它。畢竟,轉換到 iostream後,也不會忘掉。
  • 其實沒有<iostream.h>這樣的東西——標準化委員會在簡化非 C 標準標頭檔案時用<iostream>取代了它。
    • 他們這樣做的原因在條款 49 進行了解釋。還必須知道的是,如果編譯器同時支援 <iostream>和<iostream.h> ,那標頭檔案名的使用會很微妙。
    • 例如,如果使用了 #include<iostream>, 得到的是置於名稱空間 std(見條款 28)下的 iostream 庫的元素;如果使用#include <iostream.h>,得到的是置於全域性空間的同樣的元素。在全域性空間獲取元素會導致名字衝突,而設計名稱空間的初衷正是用來避免這種名字衝突的發生。還有,打字時<iostream>比<iostream.h>少兩個字,這也是很多人用它的原因。
  • 【注】參考《Effective C++》