解开一个困扰本身多时的小问题——从std::cout和endl提及

解开一个困扰本身多时的小问题

小序

今天上班的时候问了一块儿工做的Sidney同窗一个小问题,显然他是研究过了的,不过他当时没有给出我答案。这个问题着实困扰了我好长时间捏~~ios

         晚上吃的小葱蘸酱,呵呵,吃完以后气儿顺了、脑子也清醒了许多,想起这个问题没搞定,因而顺着Sidney同窗提供的线索把问题搞明白了。函数

 

正文

         问题是这样的……学习

相信下面这个程序凡是会写C++程序的同仁都认得,估计学会的第一个C++程序就是它了吧:网站



//----------------------------------------------
//        
水之真谛

// http://blog.csdn.net/FantasiaX
//----------------------------------------------

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


        
spa

         我会写一点C语言的程序,因而在写这个程序的时候就对不少东西“想固然”了。好比对于操做符“<<”,在内心一直是与C语言的printf()函数对应起来的——认为它就是封装进了ostream对象中的printf()函数。既然是这样,那么对于“endl”,天然就“想固然”地认为它是“/n”了。.net

         忽然有一天,在Visual Studio弹出的代码自动完成窗口中发现,endl不是一个成员变量(若是它表明一个字符,那么理应是一个字符类型的成员变量)而是一个成员函数!大脑中马上蹦出一个解释:或许endl函数的返回值是字符“/n”吧?但是这个答案存活了不到一秒钟就被否认了——若是想让一个函数执行从而获得它的返回值,应该是调用这个函数,因此写法应该是“std::endl()”而不是“std::endl”。写成“std::endl”是将函数名放在这里,并非在调用这个函数。哈~~脑子里的概念开始互相打架了~~
3d



         由于问题是出在了endl上,因此一直在查endl的定义——结果除了发现MSDN里有个Bug以外,一无所得L

指针

MSDN里是这样声名的:orm

template class<_Elem, _Tr>
basic_ostream<_Elem, _Tr>& endl( basic_ostream<_Elem, _Tr>& _Ostr );
xml

红色标记的地方写错了: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

不信你试试下面的代码,结果与上面的同样:

//----------------------------------------------
//        
水之真谛

// http://blog.csdn.net/FantasiaX
//----------------------------------------------

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




致谢

         感谢Sidney——谢谢你对我技术上的指导。更重要的是你提醒了我学习的方面——不要只把眼睛盯在一个地方,还要看到与它相关联的事物。还有就是要多看书,我看的书仍是太少了。

         博文视点就要三周年庆典了,也祝博文视点的朋友们万事如意、工做顺利、身体健康!

 

法律声明本文章受到知识产权法保护,任何单位或我的若须要转载此文,必需保证文章的完整性(未经做者许可的任何删节或改动将视为侵权行为)。若您须要转载,请务必注明文章出处为CSDN以保障网站的权益;请务必注明文章做者为刘铁猛http://blog.csdn.net/FantasiaX ),并向liutm@beyondsoft.com发送邮件,标明文章位置及用途。转载时请将此法律声明一并转载,谢谢!