淺析cout

來源:互聯網
上載者:User
#include<iostream>
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為例)是重載了“<<”運算子。我們現在就讓它現出函數的本來面目,請看Hello World!的等效版本:  

#include<iostream>
using namespace std;  
int main()  
{  
    cout.operator<<("Hello, World!");  
    cout.operator<<(endl);  
    return 0;  
}  

       編譯運行,結果與經典版無二。上面程式應該更容易理解了:cout是一個iostream類的對象,它有一個成員運算子函數operator<<,每次調用的時候就會向輸出裝置(一般就是螢幕啦)輸出東東。嗯,這裡有一個問題:為什麼函數operator<<能夠接受不同類型的資料,如整型、浮點型、字串甚至指標,等等呢?  
       我想你現在已經猜到了,沒錯,就是用運算子多載。運算子函數與一般函數基本無異,可以任意重載。標準庫的設計者們早已經為我們定製了iostream::operator<<對於各種C++基礎資料型別 (Elementary Data Type)的重載版本,這才使得我們這些初學者們一上來就享受到cout << "Hello, World!" << endl;的簡潔——等等,這一行是由兩個<<將"Hello, World"與"endl"操作符串連起來,那麼我們的第二版Hello, World!似乎也應該寫成:  
cout.operator<<("Hello, World!").operator<<(endl);  
才算“強等效”。究竟可不可以這樣寫?向編譯器確認一下……OK,No Problem!  

       嗯,我們已經基本上看出了cout的實質,現在不妨動動手,自己來實現一個cout的簡化版(Lite),為了區分,我們把我們設計的cout對象命名的myout,myout對象所屬的類為MyOutstream。我們要做的就是為MyOutstream類重載一系列不同類型的operator<<運算子函數,簡單起見,這裡我們僅實現了對整型(int)與字串型(char*)的重載。為了表示與iostream斷絕關係,我們不再用標頭檔iostream,而使用古老的stdio中的printf函數進行輸出,程式很簡單,包括完整的main函數,均列如下:  

#include<stdio.h>       // 在C和一些古老的C++中是stdio.h,新標準為了使標準庫  
                                 // 的標頭檔與使用者標頭檔區別開,均推薦使用不用副檔名  
                                 // 的版本,對於原有C庫,不用副檔名時標頭檔名前面要加c  

class MyOutstream  
{  
public:  
    const MyOutstream& operator<<(int value) const;  // 對整型變數的重載  
    const MyOutstream& operator<<(char* str) const; // 對字串型的重載  
};  

const MyOutstream& MyOutstream::operator<<(int value) const  
{  
    printf("%d", value);  
    return *this;                  // 注意這個返回……  
}  

const MyOutstream& MyOutstream::operator<<(char* str) const  
{  
    printf("%s", str);  
    return *this;                  // 同樣,這裡也留意一下……  
}  

MyOutstream myout;         // 隨時隨地為我們服務的全域對象myout  

int main()  
{  
    int a = 2003;  
    char* myStr = "Hello, World!";  
    myout << myStr << a << "/n";  
    return 0;  
}  

      我們已經的myout已經初具形態,可以為我們工作了。程式中的注釋指出兩處要我們特別注意的:即是operator<<函數執行完畢之後,總是返回一個它本身的引用,輸出已經完成,為何還要多此一舉?  
       還記得那個有點奇異的cout.operator<<("Hello, World!").operator<<(endl)嗎?它能實現意味著我們可以連著書寫  
cout << "Hello, World!" << endl;  
而不是   
cout << "Hello, World!";  
cout << endl;  
     為何它可以這樣連起來寫?我們分析一下:按執行順序,系統首先調用cout.operator<<("Hello, World!"),然後呢?然後cout.operator<<會返回它本身,就是說在函數的最後一行會出現類似於return *this這樣的語句,因此cout.operator<<("Hello, World!")的調用結果就返回了cout,接著它後面又緊跟著.operator<<(endl),這相當於cout.operator<<(endl)——於是又會進行下一個輸出,如果往下還有很多<<算符,調用就會一直進行……哇噢,是不是很聰明?現在你明白我們的MyOutstream::operator<<最後一行的奧妙了吧!  
       再注意一下main函數中最激動人心的那一行:  
        myout << myStr << a << "/n";  
       我們知道,最後出現的"/n"可以實現一個換行,不過我們在用C++時教程中總是有意無意地讓我們使用endl,兩者看上去似乎一樣——究竟其中有什麼玄妙?查書,書上說endl是一個操縱符(manipulator),它不但實現了換行操作,而且還對輸出緩衝區進行重新整理。什麼意思呢?原來在執行輸出操作之後,資料並非立刻傳到輸出裝置,而是先進入一個緩衝區,當適宜的時機(如裝置空閑)後再由緩衝區傳入,也可以通過操縱符flush進行強制重新整理:  
cout << "Hello, World! " << "Flush the screen now!!!" << flush;  
       這樣當程式執行到operator<<(flush)之前,有可能前面的字串資料還在緩衝區中而不是顯示在螢幕上,但執行operator<<(flush)之後,程式會強制把緩衝區的資料全部搬運到輸出裝置並將其清空。而操縱符endl相當於<< "/n" << flush的簡寫版本,它先輸出一個分行符號,再實現緩衝區的重新整理。大概這是因為一般的輸出都是以換行結尾,而結尾處又是習慣進行重新整理的時期,方便起見就把兩者結合成了endl。讀者有興趣的話,回去也可以為我們的MyOutstream實現一個類似的myflush和myendl操縱符,相關的用於重新整理C函數是fflush()。  
       不過可能在螢幕上顯示是手動重新整理與否區別看來都不大。但對於檔案等輸出對象就不大一樣了:過於頻繁的重新整理意味著老是寫盤,會影響速度。因此通常是寫入一定的位元組數後再重新整理,如何操作?靠的就是這些操縱符。  
        好了,說了這麼多,C++的iostream家族與C的print/scanf家庭相比究竟有何優勢?首先是類型處理更安全、智能,想想printf中對付int、float等的"%d"、"%f"等說明符真是多餘且麻煩,萬一用錯了搞不好還會死掉;其次是擴充性更強:我要是新定義一個複數類Complex,printf對其是無能為力,最多隻能分別輸出實、虛部,而iostream使用的<<、>>操作符都是可重載的,你只要重載相關的運算子就可以了;而且流風格的寫法也比較自然簡潔,不是嗎?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.