解開一個困擾自己多時的小問題
小序
今天上班的時候問了一起工作的 Sidney 同學一個小問題,顯然他是研究過了的,不過他當時沒有給出我答案。這個問題著實困擾了我好長時間捏 ~~
晚上吃的小蔥蘸醬,呵呵,吃完之后氣兒順了、腦子也清醒了許多,想起這個問題沒搞定,于是順著 Sidney 同學提供的線索把問題搞明白了。
正文
問題是這樣的……
相信下面這個程序凡是會寫 C++ 程序的同仁都認得, 估計學會的第一個 C++ 程序就是它了吧:
//----------------------------------------------
//
水之真諦
// http://blog.csdn.net/FantasiaX
//----------------------------------------------
#include
<iostream><br></iostream>
int
main(
int
argc,
char
*argv[])
{
std::cout "Hello, World."
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></_elem>
basic_ostream<_elem _tr>& endl( basic_ostream<_elem _tr>& _Ostr );</_elem></_elem>
紅色標記的地方寫錯了
:p
C++ ISO 文檔里是這樣聲名的:
template <class chart class traits><br></class> basic_ostream<chart>& endl(basic_ostream<chart>& os);</chart></chart>
按
MSDN
里模板的“寫法”根本編譯不過去,呵呵。
不過,
MSDN
里的說明還是非常有用的——
Terminates a line and flushes the buffer.
可是函數的功能是“結束一行并沖洗緩沖區”,如果想執行這個功能,應該是調用這個函數、應該寫
endl()
而不是
endl
啊……看來問題又繞回去了。于是這事兒就放下了。
今天遇到高手 Sidney ,又問起了這個問題。 Sidney 是研究過這個問題的,雖然沒有給出我答案,但他提到這么一句話——“ ”操作符是被重載過的,可以接收一個函數作為參數。正好前幾天我在寫《深入淺出話回調》的時候寫過類似的程序,經 Sidney 一點撥,頓時感覺豁然開朗。很快問題的答案就找到了——
1. 先查看 <iostream></iostream> 的成員,找到一個全局對象 cout
2. 查看 cout 對象,發現它是 ostream 的一個實例
3. 查看 <ostream></ostream> 文件說明中的“ ”操作符,有 10 個重載,但是沒有可將函數作為參數的
4. 仔細想了想,會不會是從別處繼承來的呢? (操作符其實就是簡寫了的函數,完全可以當函數來對待)
5. 查看 MSDN ,發現 ostream 是由類模板 basic_ostream<char char_traits> ></char> 生成的
6. 查看 basic_ostream<char char_traits> ></char> 的說明,發現它也具有“ ”操作符,并且有 15 個重載。
7.
其中的一個卸載形式是——
basic_ostream& operator
說明
cout
的
操作符可以接受一個函數指針(函數的地址)作為參數。
這個重載正好與
endl
函數的聲名相匹配,所以
后面是可以跟著
endl
的,也就是說,
cout
對象的
操作符接受到
endl
函數的地址后會在后臺調用
endl
函數,而
endl
函數會結束當前行并沖洗
buffer
。
最后啰嗦一句——你可能會問:不是函數指針嗎?為什么不寫“ std::cout ”而寫“ std::cout ”呢?實際上,函數名本身就代表的是函數的地址, &endl 與 endl 的值是一樣的 J
不信你試試下面的代碼,結果與上面的一樣:
//----------------------------------------------
//
水之真諦
// http://blog.csdn.net/FantasiaX
//----------------------------------------------
#include
<iostream><br></iostream>
int
main(
int
argc,
char
*argv[])
{
std::cout "Hello, World."
return
0;
}
致謝
感謝 Sidney ——謝謝你對我技術上的指導。更重要的是你提醒了我學習的方面——不要只把眼睛盯在一個地方,還要看到與它相關聯的事物。還有就是要多看書,我看的書還是太少了。
博文視點就要三周年慶典了,也祝博文視點的朋友們萬事如意、工作順利、身體健康!
法律聲明 : 本文章受到知識產權法保護,任何單位或個人若需要轉載此文,必需保證文章的完整性(未經作者許可的任何刪節或改動將視為侵權行為)。若您需要轉載,請務必注明文章出處為 CSDN 以保障網站的權益;請務必注明文章作者為 劉鐵猛 ( http://blog.csdn.net/FantasiaX ),并向 liutm@beyondsoft.com 發送郵件,標明文章位置及用途。轉載時請將此法律聲明一并轉載,謝謝!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
