問題引入
cout << (uf.is_same_set(x, y)) ? 'Y' : 'N'<<endl;
請問大家,這條語句對嗎?(這里的uf.is_same_set(x, y)是一個自定義函數,返回bool值;所以不是問題的關鍵)==》
答案是這條語句報錯了。我再寫了以下的語句,卻能準確無誤。
?cout << ((uf.is_same_set(x, y)) ? 'Y' : 'N') << endl;
為什么呢? ==》
1.這是因為第一條語句中,<<的優先級會高于三目運算符,即它先返回的是 ostream&;
然后會再執行ostream?'Y':'N'部分;三目判斷返回'Y'
但是'Y' 是 char 類型,不是一個可以用 << 運算的對象。
2.cout << ((uf.is_same_set(x, y)) ? 'Y' : 'N') << endl; 這個表達式會先執行三目,返回‘Y’或者‘N’;然后變為:cout<<'Y'<<endl; 所以就沒有錯。
基于這個問題,我們今天就來聊聊cout
cout是什么?
cout
是一個全局的 輸出流對象,本質上是 ostream
類的一個實例,通過 運算符重載(operator<<
),實現了類似 cout << x
這樣的打印效果。
此外還有其他流對象是:
名稱 | 類型 | 功能 |
---|---|---|
cin | istream | 標準輸入流 |
cerr | ostream | 錯誤輸出流 |
clog | ostream | 日志輸出流 |
為什么能用 <<
打印?
這是 C++ 的經典設計:通過重載運算符 <<
(插入運算符)來模擬輸出操作。它的原型類似于這樣:即它使重載的運算符函數;
ostream& operator<<(ostream& os, int value);
ostream& operator<<(ostream& os, const char* str);
ostream& operator<<(ostream& os, char c);
ostream& operator<<(ostream& os, double d);
ostream& operator<<(ostream& os, bool b);
// 等等,針對不同類型都做了重載
這就意味著你可以這樣使用:
cout << "Hello" << ' ' << 42 << ' ' << true;
它的底層執行順序是:
cout << "Hello" ? ? ? ? ?// 返回 ostream&<< ' ' ? ? ? ? ? ? // 繼續插入<< 42 ? ? ? ? ? ? ?// 繼續插入<< ' ' ? ? ? ? ? ? // ...<< true; ? ? ? ? ? // 一連串流式操作
因為每次 <<
都返回 ostream&
,所以可以鏈式調用。
輸出是怎么“流”到控制臺的?
cout
內部持有一個緩沖區(buffer),你每調用一次 <<
,其實是將數據寫入這個緩沖區。只有當你:
-
顯式使用
endl
(刷新并換行); -
或緩沖區滿;
-
或程序結束前清理資源;
才會真正把內容輸出到終端控制臺。所以我們便能理解下面兩條語句的區別了。
cout << "hello\n"; ? ? ? // 可能只是寫進緩沖區
cout << "world" << endl; // 此時強制刷新緩沖區
endl 是什么?
endl
是一個特殊的東西,它不是字符串,而是一個 函數指針,原型是:
ostream& endl(ostream& os);
它的作用是:
-
向流中插入一個換行符(
\n
); -
刷新緩沖區(flush);
所以cout << "Hello" << endl; 等價于:
cout << "Hello";
cout.put('\n');
cout.flush();
自定義類型如何支持 <<
輸出?
你可以為你的類自己寫一個 <<
重載,讓 cout << 對象
成為可能:
class Person {
public:string name;int age;
};
?
// 重載 <<
ostream& operator<<(ostream& os, const Person& p) {os << "Name: " << p.name << ", Age: " << p.age;return os;
}
這樣你就能這樣寫:
Person p{"麥兜", 20};
cout << p << endl;
ok,這個知識點實際上涉及到了運算符重載函數的編寫;我先說一個結論:
operator<<必須寫成全局函數(或友元函數),為什么呢?
因為 cout << p 是這樣調用的:operator<<(cout, p); // ostream 是左邊,Person 是右邊
如果你寫成:
class Person { ostream& operator<<(ostream& os); // 錯誤!只能是 p << cout
};
這就變成了 p << cout,方向顛倒了,所以沒法實現 cout << p 的語法。
故哪些運算符建議寫成成員函數?哪些建議全局?
運算符類型 | 推薦形式 | 說明 |
---|---|---|
= , [] , () , -> | 成員函數 | 這些操作符需要訪問類的內部狀態或作用于“左側對象本身” |
<< , >> , + , - , == , != | 全局函數或友元函數 | 左側不是類對象(如 cout << obj ),或需要雙操作數對稱性 |
cout的使用技巧
-
運算符優先級陷阱 (如我們上面的問題)
-
輸出 bool 值時注意格式
cout << true << endl; ? ? ?// 輸出: 1
cout << boolalpha << true << endl; ?// 輸出: true ?
? ?3.輸出字符和整數要區
char ch = 'A';
int x = 65;
cout << ch << endl; ? ? // 輸出: A
cout << static_cast<int>(ch) << endl; ?// 輸出: 65
4.其他格式化技巧:
我們需要加上 頭文件: #include <iomanip>
1) 設置小數精度 ;如上
2) 寬度對齊 & 填充字符
cout << setw(10) << 42 << endl; ? ? ? ? // 寬度為10,默認右對齊
cout << setfill('*') << setw(10) << 42 << endl; ?// 輸出:********42
3)左右對齊
cout << left << setw(10) << 42 << endl; ? // 左對齊
cout << right << setw(10) << 42 << endl; ?// 右對齊
4) 輸出十六進制/八進制
cout << hex << 255 << endl; ?// ff
cout << oct << 255 << endl; ?// 377
cout << dec << 255 << endl; ?// 255 (恢復十進制)
顯示符號位(正數也帶 + )
cout << showpos << 123 << endl; ?// +123
再次理解
運算符重載是指可以自定義某個類使用某個運算符的邏輯,譬如+,-,*等等。
而<<這個運算符也是可以被重載的;例如ostream類就是重載了這個運算符 。
cout是ostream的一個對象,它使用<<能夠將數據寫入這個緩沖區。
實際上cout<<10是operator<<(cout, 10);的語法糖。即他本質上是調用了operator<<,并把要打印的東西(整數,浮點數,字符,字符串)作為參數。
而這個運算符重載函數返回的是ostream&,即引用類型,則他可以實現鏈式調用;