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

本文探讨了C++中endl的实现原理,解答了为何endl不是成员变量而是成员函数的问题,并详细解析了其如何通过操作符重载实现换行与刷新缓冲区。

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

小序

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

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

 

正文

         问题是这样的……

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



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

// http://blog.youkuaiyun.com/FantasiaX
//----------------------------------------------

#include

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
basic_ostream & endl(basic_ostream & os);

MSDN里模板的“写法”根本编译不过去,呵呵。

         不过,MSDN里的说明还是非常有用的——Terminates a line and flushes the buffer. 可是函数的功能是“结束一行并冲洗缓冲区”,如果想执行这个功能,应该是调用这个函数、应该写endl()而不是endl啊……看来问题又绕回去了。于是这事儿就放下了。

         今天遇到高手Sidney,又问起了这个问题。Sidney是研究过这个问题的,虽然没有给出我答案,但他提到这么一句话——“<<”操作符是被重载过的,可以接收一个函数作为参数。正好前几天我在写《深入浅出话回调》的时候写过类似的程序,经Sidney一点拨,顿时感觉豁然开朗。很快问题的答案就找到了——

1.         先查看 的成员,找到一个全局对象cout

2.         查看cout对象,发现它是ostream的一个实例

3.         查看 文件说明中的“<<”操作符,有10个重载,但是没有可将函数作为参数的

4.         仔细想了想,会不会是从别处继承来的呢?(操作符其实就是简写了的函数,完全可以当函数来对待)

5.         查看MSDN,发现ostream是由类模板basic_ostream > 生成的

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

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

最后啰嗦一句——你可能会问:不是函数指针吗?为什么不写“std::cout<<&endl”而写“std::cout< ”呢?实际上,函数名本身就代表的是函数的地址,&endlendl的值是一样的J

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

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

// http://blog.youkuaiyun.com/FantasiaX
//----------------------------------------------

#include

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




致谢

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

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

 

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




### `std::cout`、`std`、`::` `count` 的含义与用法 在 C++ 中,`std::cout` 是标准库中用于输出数据到控制台的对象。它属于 `std` 命名空间,该命名空间是 C++ 标准库的核心组成部分之一,用于将标准库中的函数、类对象与用户定义的名称隔离开来,从而避免命名冲突。使用 `std::cout` 时,必须通过作用域解析运算符 `::` 来访问 `std` 命名空间中的成员 [^2]。 ```cpp #include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; } ``` 在上述代码中,`std::cout` 用于输出字符串到控制台,`std::endl` 用于插入换行符并刷新输出缓冲区。 ### `std` 命名空间的作用 `std` 是 C++ 标准库的默认命名空间。所有标准库中的组件,如 `std::vector`、`std::map`、`std::string` 等,都定义在该命名空间中。命名空间的引入解决了全局命名冲突的问题,尤其是在大型项目或多个库共存的情况下 [^2]。 在程序中,可以通过 `using namespace std;` 将整个 `std` 命名空间引入当前作用域,也可以通过 `using std::cout;` 只引入特定的标识符。然而,引入整个命名空间可能会导致名称冲突,因此在实际开发中建议仅引入必要的标识符 [^4]。 ```cpp #include <iostream> int main() { using std::cout; using std::endl; cout << "Hello, World!" << endl; return 0; } ``` ### 作用域解析运算符 `::` 的使用 作用域解析运算符 `::` 用于访问命名空间、类或结构体中的成员。它可以明确指定某个标识符属于哪个命名空间或类,从而避免名称冲突。例如,在 `std::cout` 中,`::` 表示 `cout` 属于 `std` 命名空间 [^2]。 ```cpp namespace MyNamespace { int count = 0; } int main() { int count = 10; std::cout << "Local count: " << count << std::endl; std::cout << "MyNamespace::count: " << MyNamespace::count << std::endl; return 0; } ``` 在上述代码中,`MyNamespace::count` 用于访问命名空间 `MyNamespace` 中的 `count` 变量,而局部变量 `count` 则直接访问。 ### `count` 变量的含义与作用域 在 C++ 中,`count` 是一个合法的标识符,通常用作变量名。它可以在全局作用域、局部作用域或命名空间中定义。例如,`std::count` 是 `<algorithm>` 头文件中定义的函数,用于统计某个值在范围内出现的次数 [^5]。 ```cpp #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> vec = {1, 2, 3, 2, 4, 2}; int cnt = std::count(vec.begin(), vec.end(), 2); std::cout << "Number of 2s: " << cnt << std::endl; return 0; } ``` 在上述代码中,`std::count` 统计了 `vec` 中值为 `2` 的元素个数,并将结果存储在 `cnt` 中。 ### 命名空间与全局作用域的关系 当使用 `using namespace std;` 时,`std` 命名空间中的所有标识符都会被引入当前作用域,但不会覆盖已有的同名标识符,而是作为补充 [^1]。例如,如果在 `std` 命名空间之外定义了 `count`,则局部 `count` 会优先被访问。 ```cpp #include <iostream> int count = 100; int main() { using namespace std; cout << "Global count: " << count << endl; return 0; } ``` 在上述代码中,`count` 是全局变量,`using namespace std;` 引入了 `std` 命名空间中的所有标识符,但由于 `count` 已经在全局作用域中定义,因此不会被覆盖 [^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值