`

解开一个困扰自己多时的小问题——从std::cout和endl说起

 
阅读更多
<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog336280.html" frameborder="0" width="336" scrolling="no" height="280"></iframe>

解开一个困扰自己多时的小问题

小序

今天上班的时候问了一起工作的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>&amp; endl( basic_ostream<_elem _tr>&amp; _Ostr );</_elem></_elem>

红色标记的地方写错了:p

C++ ISO文档里是这样声名的:

template <class chart class traits><br></class>basic_ostream<chart>&amp; endl(basic_ostream<chart>&amp; 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> &gt;</char>生成的

6. 查看basic_ostream<char char_traits> &gt;</char>的说明,发现它也具有“”操作符,并且有15个重载。

7. 其中的一个卸载形式是——
basic_ostream& operator
说明cout操作符可以接受一个函数指针(函数的地址)作为参数。
这个重载正好与endl函数的声名相匹配,所以后面是可以跟着endl的,也就是说,cout对象的操作符接受到endl函数的地址后会在后台调用endl函数,而endl函数会结束当前行并冲洗buffer

最后啰嗦一句——你可能会问:不是函数指针吗?为什么不写“std::cout”而写“std::cout”呢?实际上,函数名本身就代表的是函数的地址,&endlendl的值是一样的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发送邮件,标明文章位置及用途。转载时请将此法律声明一并转载,谢谢!




分享到:
评论

相关推荐

    C++ Primer 源程序

    #include #include "Sales_item.h" int main() ... std::cout &lt;&lt; item1 + item2 &lt;&lt; std::endl; return 0; // indicate success } else { std::cerr &lt;&lt; std::endl; return -1; // indicate failure }

    关于"Hello World!"程序

    std::cout.operator(std::endl); // member endl是函数模板指针 std::operator(std::cout, "Hello, world!").operator(std::endl); char ch; std::operator&gt;&gt;(std::cin, ch); return 0; }

    计算两个整数的最小公倍数.docx

    std::cout 请输入第一个整数: "; // std::cin &gt;&gt; num1; std::cout 请输入第二个整数: "; // std::cin &gt;&gt; num2; int result = lcm(num1, num2); std::cout 它们的最小公倍数是: " &lt;&lt; result &lt;&lt; std::endl;...

    后缀表达式c++实现代码

    std::cout &lt;&lt; "src: " &lt;&lt; src &lt;&lt; std::endl; Expression expression; Expression::PrefixType result; int ret = expression.ToPrefix(src, result); if (ret != 0) { std::cout ...

    学生信息管理系统.rar

    std::cout &lt;&lt; "Please choose an operation:\n"; std::cout [1] List all students\n"; std::cout [2] Display information of a student\n"; std::cout [3] Add a student\n"; std::cout [4] Remove a ...

    为什么一些人写c++代码,前面会加上std::ios::sync_with_stdio(false); cin.tie(0);这两句话??

    可以增强cin和cout的效率。 在做acm一些题时,经常出现 数据集超大造成 cin读入过多 超时的情况。 这是因为在c++中cin,cout虽然方便但是效率低。 是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段...

    使用c++实现boost::any类

    std::cout &lt;&lt; "b2= " &lt;&lt; b2 &lt;&lt; std::endl; any any_str(std::string("abc")); std::string msg = any_cast&lt;std::string&gt;(any_str); std::cout &lt;&lt; "msg= " &lt;&lt; msg &lt;&lt; std::endl; std::vector&lt;float&gt; values,...

    http-master 源码

    auto coro = coro::start([]{ try { ... "{ \"UserId\": \"matt\", \"Password\": \"matt\"} " ); auto sessionId = res.... std::cout () &lt;&lt; std::endl; std::cout () &lt;&lt; std::endl; std::cout () &lt;&lt; std::endl;

    http 源码 http 源码

    auto coro = coro::start([]{ try { ... "{ \"UserId\": \"matt\", \"Password\": \"matt\"} " ); auto sessionId = res.... std::cout () &lt;&lt; std::endl; std::cout () &lt;&lt; std::endl; std::cout () &lt;&lt; std::endl;

    bcrypt库C/C++使用

    bcrypt库C/C++使用 ```cpp #include "bcrypt/... std::cout &lt;&lt; BCrypt::validatePassword(password,hash) &lt;&lt; std::endl; std::cout &lt;&lt; BCrypt::validatePassword("123456",hash) &lt;&lt; std::endl; return 0; } ```

    c++性能测试精确到秒、毫秒、纳秒

    c++性能测试精确到秒、毫秒、纳秒 #include #include #include void Run() { for (int i = 0; i ;... auto beforeTime = std::... std::cout 秒" &lt;&lt; std::endl; //毫秒级 double duration_millsecond = std::ch

    cout不打印,不显示,不输出 怎么办?

    解决cout不显示,不输出,不打印

    斐波那契数列及C++代码实现收藏学习.docx

    斐波那契数列 斐波那契数列是一个经典的数列,其中每个数字是前两个数字的和。... std::cout &lt;&lt; std::endl; return 0; } ``` 然而,上面的递归方法对于较大的`n`来说是非常低效的,因为它会重复计

    C++(第一章).pdf

    【注意: using 是一个关键字】 若没有该定义: std:: ———————————————— 版权声明:本文为CSDN博主「嗜血魔樽」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接...

    FLV解复用代码实现!!!!

    std::cout 写入视频size:" &lt;&lt; pkt-&gt;size &lt;&lt; std::endl; //添加start code,注意:MP4包不带start code ts包是带的。带的话也能调用该接口,不会重复添加 std::cout 视频时间戳:" &lt;&lt; pkt-&gt;pts &lt;&lt; ...

    使用`stdvector`来创建二维数组(通常称为二维向量).pdf

    ### 一、创建二维向量 首 先 , 我 们 需 要 包 含 必 要 的 头 文 件 , 并 创 建 一 个 二 维 向 量 。 这 里 我 们 使 用 ...std::cout 行数: " () &lt;&lt; std::endl; for (const auto& row : matrix) { std::

    C++17 使用 std::string_view避免字符串拷贝优化程序性能

    C++中std::string是日常Coding中经常使用的一个类,使用起来非常方便,但是也存在一些弊端。 如下代码,参数传递的过程发生了内存分配(Memory Allocation)和内存拷贝。 void fun(const std::string& s) { std::...

    自明排课9.0

    std::cout &lt;&lt;std::dec&lt;&lt;x&lt;&lt;std::endl; system("pause"); return 0; } 如果是参数7533就安下面的算法 #include int main() { unsigned long int x=0x3c39925f; x=((~((x+0x19770523) ^ 0x330f222d))*0x...

Global site tag (gtag.js) - Google Analytics