C++入門解惑(1)——淺析cout (轉)
和大多數朋友一樣,我頭一遭遇到cout是在生平第一個看到C++——經典的“Hello, World!”中,作為我如今最擅長編寫的程式之一(^_^),它大概是這樣子的:
#include
using namespace std;
int main()
{
cout << "Hello, World!" << endl;
return 0;
}
由於以前學過C,所以這段程式碼的其它部分在我看來都還算“正常”,然而cout卻很獨特:既不是,似乎也不是C++特別規定出來的像if,for一類有特殊語法的“語句”。由於只是初步介紹,所以那本書只是簡單的說cout是C++中的“標準輸入輸出流”……這於我而言實在是一個很深奧的術語。這還沒完,之後又遇見了cin……因為不知底細,從此使用它們的時候都誠惶誠恐,幾欲逃回C時代那簡明的printf(),畢竟好歹我可以說:我在的是一個函式。那有著一長串<>的玩意,究竟算怎麼回事呢?我一直想把它們當作關鍵字,可偏偏不是,而且居然是用C++語言“做”出來的,呵!但printf()用多了就開始有人好心地批判我的程式“C語言痕跡過重”……
後來隨著學習的深入,總算大概明白了cout/cin/cerr/...的鬼把戲:那些東東不過是變著法兒“哄人”,其實說到底還是函式呼叫,不過這函式有些特殊,用的是運算子過載,確切地說(以下還是以cout為例)是過載了“<
#include
using namespace std;
int main()
{
cout.operator< cout.operator< return 0;
}
編譯執行,結果與經典版無二。上面程式應該更容易理解了:cout是一個iostream類的物件,它有一個成員運算子函式operator< 我想你現在已經猜到了,沒錯,就是用運算子過載。運算子函式與一般函式基本無異,可以任意過載。標準庫的設計者們早已經為我們定製了iostream::operator<cout.operator<才算“強等效”。究竟可不可以這樣寫?向確認一下……OK,No Problem!
嗯,我們已經基本上看出了cout的實質,現在不妨動動手,自己來實現一個cout的簡化版(Lite),為了區分,我們把我們設計的cout物件命名的myout,myout物件所屬的類為MyOutstream。我們要做的就是為MyOutstream類過載一系列不同型別的operator<
#include
// 的標頭檔案與標頭檔案區別開,均推薦使用不用副檔名
// 的版本,對於原有C庫,不用副檔名時標頭檔案名前面要加c
class MyOutstream
{
public:
const MyOutstream& operator< const MyOutstream& operator<};
const MyOutstream& MyOutstream::operator<{
printf("%d", value);
return *this; // 注意這個返回……
}
const MyOutstream& MyOutstream::operator<{
printf("%s", str);
return *this; // 同樣,這裡也留意一下……
}
MyOutstream myout; // 隨時隨地為我們服務的全域性物件myout
int main()
{
int a = ;
char* myStr = "Hello, World!";
myout << myStr << a << "
";
return 0;
}
我們已經的myout已經初具形態,可以為我們工作了。程式中的註釋指出兩處要我們特別注意的:即是operator< 還記得那個有點奇異的cout.operator<cout << "Hello, World!" << endl;
而不是
cout << "Hello, World!";
cout << endl;
為何它可以這樣連起來寫?我們分析一下:按執行順序,首先呼叫cout.operator<然後cout.operator<,就是說在函式的最後一行會出現類似於return *this這樣的語句,因此cout.operator<的呼叫結果就返回了cout,接著它後面又緊跟著.operator< 再注意一下main函式中最激動人心的那一行:
myout << myStr << a << "
";
我們知道,最後出現的"
"可以實現一個換行,不過我們在用C++時教程中總是有意無意地讓我們使用endl,兩者看上去似乎一樣——究竟其中有什麼玄妙?查書,書上說endl是一個操縱符(manipulator),它不但實現了換行操作,而且還對輸出緩衝區進行重新整理。什麼意思呢?原來在執行輸出操作之後,資料並非立刻傳到輸出裝置,而是先進入一個緩衝區,當適宜的時機(如裝置空閒)後再由緩衝區傳入,也可以透過操縱符flush進行強制重新整理:
cout << "Hello, World! " << "Flush the screen now!!!" << flush;
這樣當程式執行到operator< 不過可能在螢幕上顯示是手動重新整理與否區別看來都不大。但對於檔案等輸出物件就不大一樣了:過於頻繁的重新整理意味著老是寫盤,會影響速度。因此通常是寫入一定的位元組數後再重新整理,如何操作?靠的就是這些操縱符。
好了,說了這麼多,C++的iostream家族與C的print/scanf家庭相比究竟有何優勢?首先是型別處理更、智慧,想想printf中對付int、float等的"%d"、"%f"等說明符真是多餘且麻煩,萬一用錯了搞不好還會死掉;其次是擴充套件性更強:我要是新定義一個複數類Complex,printf對其是無能為力,最多隻能分別輸出實、虛部,而iostream使用的<>運算子都是可過載的,你只要過載相關的運算子就可以了;而且流風格的寫法也比較自然簡潔,不是麼?
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-958816/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Vue入門淺析Vue
- Spring入門系列:淺析知識點Spring
- C++輸入輸出常用格式(cin,cout,stringstream)C++
- 淺析氣泡排序-c++排序C++
- C++ remove erase 用法淺析C++REM
- 淺入kubernetes(1):Kubernetes 入門基礎
- C++(std::cout 處理 char*)C++
- J2SE入門(五) final關鍵字淺析
- JavaScript入門②-函式(1)基礎{淺出}JavaScript函式
- C++入門教程C++
- 淺析微信掃碼登入原理
- JS指令碼非同步載入淺析JS指令碼非同步
- C++ cout列印輸出 (解決輸出亂碼)C++
- iOS Block淺淺析iOSBloC
- C++快速入門+20201011C++
- 由淺到淺入門批量渲染(一)
- 由淺到淺入門批量渲染(二)
- 由淺到淺入門批量渲染(完)
- 由淺到淺入門批量渲染(四)
- 淺析依賴倒轉、控制反轉、IoC 容器、依賴注入。依賴注入
- 淺析SpringBoot載入配置的6種方式Spring Boot
- 淺析node.js的模組載入Node.js
- JavaScript 隱性型別轉換步驟淺析JavaScript型別
- 淺析RedisRedis
- Jvm 淺析JVM
- CGLib淺析CGLib
- 淺析XMLXML
- MongoDB淺析MongoDB
- 淺析 JWTJWT
- 淺析 DDD
- RunLoop 淺析OOP
- 淺析 ReentrantLockReentrantLock
- Unstated淺析
- 淺析SharedPreferences
- Nginx淺析Nginx
- 淺析PromisePromise
- ejs 淺析JS
- 淺析KubernetesStatefulSet
- AIDL淺析AI