一旦讲到这个函数,就要涉及到输入输出流。展示 基本输入输出类的继承结构 的最简明的方法就是用图片,先来看两张图片:
好,下面进入正题。
先来看一下std::endl这个模板函数的本来面目,即它的声明:
template <class charT, class traits>
basic_ostream<charT,traits>& endl (basic_ostream<charT,traits>& os);
对于ostream输出流,有特化的版本:
ostream& endl (ostream& os);
我们还是来研究一般的版本。
对该版本的一个等价的实现,据我猜测应该是这样的:
template <class charT, class traits>
basic_ostream<charT,traits>& endl (basic_ostream<charT,traits>& os)
{
os.put(os.widen('\n'));
os.flush();
return os;
}
其中,用到的widen函数是从basic_ostream的基类basic_ios继承来的。
std::basic_ios::widen
char_type widen (char c) const;
char_type
(generally, a wide character type).
从上面的描述中可以看出,widen函数就是把窄字符变成对应的char_type类型的字符,如果char_type还是char,那么什么也不做,如果char_type是wchar_t类型,则就将窄字符换算成宽字符。
std::basic_ostream::flush
basic_ostream& flush();
For stream buffer objects that implement intermediate buffers, this function requests all characters to be written to the controlled sequence.该函数的作用是将与当前输出对象关联的缓存区输出去指定的控制序列中去,并 清空。
好了,这下全都清楚了。
所谓std::endl,原来就是一个模板函数名,相当于函数指针啊。
该函数以引用的方式接收一个输出对象,经过处理后,再以引用的方式返回该对象本身。
======================================================================
有了以上的分析,我们就可以来做个小demo来验证下。
#include <iostream>
int main(void)
{
endl(std::cout << "Test 4 std::endl");
endl(std::cout << "Here is the 2nd Line!");
}
请不要对endl前面不加std命名空间限定符感到不解,请查看Koenig looup法则,或者叫ADL法则,即如果找不到函数的声明或定义,就到实参所在的空间中去查找。EC++里面有讲到。
上述程序的输出结果有三行;
1) Test 4 std::endl
2) Here is the 2nd Line!
3)
接受了endl是函数名 这个概念之后,我们继续。
如果在平常使用中,都按照上面代码那样去写,那可就是很坑爹了。完全反直觉。为此,我们要重载<<运算符,来满足直觉感 这一要求。
请看下面这三个重载函数:
basic_ostream& operator<< (basic_ostream& (*pf)(basic_ostream&)); basic_ostream& operator<< (basic_ios<char_type,traits_type>& (*pf)(basic_ios<char_type,traits_type>&)); basic_ostream& operator<< (ios_base& (*pf)(ios_base&));
这三个重载函数接收两个参数,第一个以引用的方式接收一个basic_ostream对象(因为不能拷贝),第二个参数为一个函数指针,该函数指针指向一个函数,该函数以引用的方式接收一个流对象,经过处理后,返回该流对象本身。
这样,再加上ADL法则(Koenig looup法则),就可以写成如下的形式了:
std::cout << "This is how std::endl work!" << std::endl;
搞定收工。