于无声处听惊雷,与无色处见繁花。
endl应该是学习C++遇见的第一个小玩意儿,其实endl蕴含的东西还挺多的。
#include<iostream>
using namespace std;
int main() {
cout << "what's essence of emdl ?" << endl;
system("pause");
}
打印“ ”中的字符串后换行,这是我们能看到的endl的作用。
stackoverflow里有人说
std::cout << std::endl;/*等价于*/std::cout << '\n' << std::flush;
// MANIPULATORS
template<class _Elem,
class _Traits> inline
basic_ostream<_Elem, _Traits>&
__CLRCALL_OR_CDECL endl(basic_ostream<_Elem, _Traits>& _Ostr)
{ // insert newline and flush stream
_Ostr.put(_Ostr.widen('\n'));
_Ostr.flush();
return (_Ostr);
}
转到定义 可以看到 endl实现了两个功能一个是‘\n’换行,另一个是flush刷新,其中刷新输出流指的是将缓冲区的数据全部传递到输出设备并将输出缓冲区清空。
明明实现了函数的功能 为什么是叫 endl 而不是endl()?
endl是一个函数模板,再被使用时会实例化为模板函数。但是函数调用应该使用一对圆括号,也就是写成endl()的形式,而在语句cout<<”Hello world”<<endl;
中并没有这样。在头文件iostream中,有这样一条申明语句:extern ostream& cout;这说明cout是一个ostream类对象。而<<原本是用于移位运算的操作符,在这里用于输出,说明它是一个经过重载的操作符函数。如果把endl当做一个模板函数,那么cout<<endl可以解释成cout.operator<<(endl);
于是我们试了试这样来操作一波。
cout << "what's essence of endl ?" << (endl);
程序运行无误,验证成功。
由于一个函数名代表一个函数的入口地址,所以在cout的所属类ostream中应该有一个operator<<()函数的重载形式接受一个函数指针做参数。
查找ostream类的定义,发现其实是另一个类模板实例化之后生成的模板类:
typedef basic_ostream<char, char_traits<char> > ostream;
在头文件ostream中查找basic_ostream的定义,其中一次重载。
typedef basic_ostream<_Elem, _Traits> _Myt;
_Myt& __CLR_OR_THIS_CALL operator<<(_Myt& (__cdecl *_Pfn)(_Myt&))
{ // call basic_ostream manipulator
_DEBUG_POINTER(_Pfn);
return ((*_Pfn)(*this));
}
ostream& ostream::operator<<(ostream& (*op)(ostream&))
{
return (*op)(*this);
}
这个重载正好与endl函数的声明相匹配,所以<<后面是可以跟着endl 。也就是说,cout对象的<<操作符接收到endl函数的地址后会在重载的操作符函数内部调用endl函数,而endl函数会结束当前行并刷新输出缓冲区。
对于一个函数而言,函数名本身就代表函数的入口地址,而函数名前加&也代表函数的入口地址。
对此,我们又可以继续玩出一些花样
cout << "what's essence of endl ?" <<&endl;
程序运行正确。
总结:
endl有两个功能1:换行,2:刷新缓冲区
endl的之所以是endl,是因为本身是一个函数模板,加上<<运算符重载,而变成的方便易用的endl
---------------------
参考:https://blog.csdn.net/K346K346/article/details/49981695