解開一個困擾本身多時的小問題

小序

今天上班的時候問了一塊兒工做的Sidney同窗一個小問題,顯然他是研究過了的,不過他當時沒有給出我答案。這個問題着實困擾了我好長時間捏~~
         晚上吃的小蔥蘸醬,呵呵,吃完以後氣兒順了、腦子也清醒了許多,想起這個問題沒搞定,因而順着Sidney同窗提供的線索把問題搞明白了。
 

正文

         問題是這樣的……
相信下面這個程序凡是會寫C++程序的同仁都認得,估計學會的第一個C++程序就是它了吧:


//----------------------------------------------
//        
水之真諦

// [url]http://blog.csdn.net/FantasiaX[/url]
//----------------------------------------------

#include
<iostream>
int main(int argc, char *argv[])
{
         std::cout << "Hello, World." << std::endl;
         return 0;
}


        
         我會寫一點C語言的程序,因而在寫這個程序的時候就對不少東西「想固然」了。好比對於操做符「<<」,在內心一直是與C語言的printf()函數對應起來的——認爲它就是封裝進了ostream對象中的printf()函數。既然是這樣,那麼對於「endl」,天然就「想固然」地認爲它是「\n」了。
         忽然有一天,在Visual Studio彈出的代碼自動完成窗口中發現,endl不是一個成員變量(若是它表明一個字符,那麼理應是一個字符類型的成員變量)而是一個成員函數!大腦中馬上蹦出一個解釋:或許endl函數的返回值是字符「\n」吧?但是這個答案存活了不到一秒鐘就被否認了——若是想讓一個函數執行從而獲得它的返回值,應該是調用這個函數,因此寫法應該是「std::endl()」而不是「std::endl」。寫成「std::endl」是將函數名放在這裏,並非在調用這個函數。哈~~腦子裏的概念開始互相打架了~~


         由於問題是出在了endl上,因此一直在查endl的定義——結果除了發現MSDN裏有個Bug以外,一無所得L

MSDN裏是這樣聲名的:
template class<_Elem, _Tr>
basic_ostream<_Elem, _Tr>& endl( basic_ostream<_Elem, _Tr>& _Ostr );
紅色標記的地方寫錯了:p

C++ ISO文檔裏是這樣聲名的:
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
MSDN裏模板的「寫法」根本編譯不過去,呵呵。

         不過,MSDN裏的說明仍是很是有用的——Terminates a line and flushes the buffer. 但是函數的功能是「結束一行並沖洗緩衝區」,若是想執行這個功能,應該是調用這個函數、應該寫endl()而不是endl啊……看來問題又繞回去了。因而這事兒就放下了。

         今天遇到高手Sidney,又問起了這個問題。Sidney是研究過這個問題的,雖然沒有給出我答案,但他提到這麼一句話——「<<」操做符是被重載過的,能夠接收一個函數做爲參數。正好前幾天我在寫《深刻淺出話回調》的時候寫過相似的程序,經Sidney一點撥,頓時感受豁然開朗。很快問題的答案就找到了——
1.         先查看<iostream>的成員,找到一個全局對象cout
2.         查看cout對象,發現它是ostream的一個實例
3.         查看<ostream>文件說明中的「<<」操做符,有10個重載,可是沒有可將函數做爲參數的
4.         仔細想了想,會不會是從別處繼承來的呢?(操做符其實就是簡寫了的函數,徹底能夠當函數來對待)
5.         查看MSDN,發現ostream是由類模板basic_ostream<char, char_traits<char> >生成的
6.         查看basic_ostream<char, char_traits<char> >的說明,發現它也具備「<<」操做符,而且有15個重載。
7.         其中的一個卸載形式是——
basic_ostream& operator << ( basic_ostream& (*_Pfn)(basic_ostream&) );
說明cout<<操做符能夠接受一個函數指針(函數的地址)做爲參數。
這個重載正好與endl函數的聲名相匹配,因此<<後面是能夠跟着endl的,也就是說,cout對象的<<操做符接受到endl函數的地址後會在後臺調用endl函數,而endl函數會結束當前行並沖洗buffer

最後囉嗦一句——你可能會問:不是函數指針嗎?爲何不寫「std::cout<<&endl」而寫「std::cout<<endl」呢?實際上,函數名自己就表明的是函數的地址,&endlendl的值是同樣的J
不信你試試下面的代碼,結果與上面的同樣:
//----------------------------------------------
//        
水之真諦

// [url]http://blog.csdn.net/FantasiaX[/url]
//----------------------------------------------

#include
<iostream>
int main(int argc, char *argv[])
{
         std::cout << "Hello, World." << &std::endl;
         return 0;
}




致謝

         感謝Sidney——謝謝你對我技術上的指導。更重要的是你提醒了我學習的方面——不要只把眼睛盯在一個地方,還要看到與它相關聯的事物。還有就是要多看書,我看的書仍是太少了。
         博文視點就要三週年慶典了,也祝博文視點的朋友們萬事如意、工做順利、身體健康!

 
法律聲明本文章受到知識產權法保護,任何單位或我的若須要轉載此文,必需保證文章的完整性(未經做者許可的任何刪節或改動將視爲侵權行爲)。若您須要轉載,請務必註明文章出處爲51cto和CSDN以保障網站的權益;請務必註明文章做者爲劉鐵猛[url]http://blog.csdn.net/FantasiaX[/url] ),並向[email]liutm@beyondsoft.com[/email]發送郵件,標明文章位置及用途。轉載時請將此法律聲明一併轉載,謝謝!
相關文章
相關標籤/搜索