C++学习记录20--endl,'\n',"\n"
2017-11-20 09:50
295 查看
转自:
http://blog.csdn.net/k346k346/article/details/49981695
http://blog.csdn.net/github_35383443/article/details/52040312
由于endl会导致输出的文字换行,自然而然地我们会想到endl可能就是换行符’\n’。
但是,如果我们定义char c=endl;会得到一个编译错误,这说明endl并不是一个字符,所以应该到系统头文件中去查找endl的定义。通过VS2012转到定义,找到了endl的定义如下:
从定义中看出,endl是一个函数模板,它实例化之后变成一个模板函数,其作用如这个函数模板的注释所示,插入换行符并刷新输出流。其中刷新输出流指的是将缓冲区的数据全部传递到输出设备并将输出缓冲区清空。
在头文件iostream中,有这样一条申明语句:extern ostream& cout;这说明cout是一个ostream类对象。而<<原本是用于移位运算的操作符,在这里用于输出,说明它是一个经过重载的操作符函数。如果把endl当做一个模板函数,那么cout<
所以,实际上应该在类模板basic_ostream中查找operator<<()的重载版本。在头文件ostream中查找basic_ostream的定义,发现其中operator<<作为成员函数被重载了17次,其中的一种:
在ostream类中,operator<<作为成员函数重载方式如下:
这个重载正好与endl函数的申明相匹配,所以<<后面是可以跟着endl 。也就是说,cout对象的<<操作符接收到endl函数的地址后会在重载的操作符函数内部调用endl函数,而endl函数会结束当前行并刷新输出缓冲区。
为了证明endl是一个 函数模板,或者说endl是一个经过隐式实例化之后的模板函数,我们把程序改造如下:
这个程序可以正常运行,并且结果完全同上一个程序。原因是对于一个函数而言,函数名本身就代表函数的入口地址,而函数名前加&也代表函数的入口地址。
在< iomanip>头文件中定义的操作符有:
这些格式控制符大致可以替代ios的格式函数成员的功能,且使用比较方便。例如,为了把整数345按16进制输出,可以采用两种方式:
或者:
可以看出采用格式操纵符比较方便,二者的区别主要在于:格式成员函数是标准输出对象cout的成员函数,因此在使用时必须和cout同时出现,而操纵符是自由函数,可以独立出现,使用格式成员函数要显示采用函数调用的形式,不能用IO运算符”<<”和”>>”形成链式操作。
输出流自定义操纵符:
输入流自定义操纵符:
示例代码如下:
程序中OutputNo和InputHex都是用户自定义的格式操纵符,操作符的函数原型必须满足cout对象的成员函数operator<<()的重载形式
ostream& ostream::operator<<(ostream& (*op)(ostream&));
在c++中,终端输出换行时,用
在没有必要刷新输出流的时候应尽量使用
http://blog.csdn.net/k346k346/article/details/49981695
http://blog.csdn.net/github_35383443/article/details/52040312
一、endl说明
1、endl的本质
cout<<”Hello world”<<endl;
由于endl会导致输出的文字换行,自然而然地我们会想到endl可能就是换行符’\n’。
但是,如果我们定义char c=endl;会得到一个编译错误,这说明endl并不是一个字符,所以应该到系统头文件中去查找endl的定义。通过VS2012转到定义,找到了endl的定义如下:
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是一个函数模板,它实例化之后变成一个模板函数,其作用如这个函数模板的注释所示,插入换行符并刷新输出流。其中刷新输出流指的是将缓冲区的数据全部传递到输出设备并将输出缓冲区清空。
2、cout<< endl的介绍
endl是一个函数模板,再被使用时会实例化为模板函数。但是函数调用应该使用一对圆括号,也就是写成endl()的形式,而在语句cout<<”Hello world”<<endl;中并没有这样,原因何在?
在头文件iostream中,有这样一条申明语句:extern ostream& cout;这说明cout是一个ostream类对象。而<<原本是用于移位运算的操作符,在这里用于输出,说明它是一个经过重载的操作符函数。如果把endl当做一个模板函数,那么cout<
typedef basic_ostream<char, char_traits<char> > ostream;
所以,实际上应该在类模板basic_ostream中查找operator<<()的重载版本。在头文件ostream中查找basic_ostream的定义,发现其中operator<<作为成员函数被重载了17次,其中的一种:
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类中,operator<<作为成员函数重载方式如下:
这个重载正好与endl函数的申明相匹配,所以<<后面是可以跟着endl 。也就是说,cout对象的<<操作符接收到endl函数的地址后会在重载的操作符函数内部调用endl函数,而endl函数会结束当前行并刷新输出缓冲区。
为了证明endl是一个 函数模板,或者说endl是一个经过隐式实例化之后的模板函数,我们把程序改造如下:
#include <iostream> using namespace std; int main() { cout<<"Hello world"<<&endl; }
这个程序可以正常运行,并且结果完全同上一个程序。原因是对于一个函数而言,函数名本身就代表函数的入口地址,而函数名前加&也代表函数的入口地址。
3、endl其实是IO操纵符
实际上,endl被称为IO操纵符,也有翻译成IO算子的。IO操作符的本质是自由函数,他们并不封装在某个类的内部,使用时不采用显示的函数调用的形式。在< iostream>头文件中定义的操纵符有:endl:输出时插入换行符并刷新流 endls:输出时在字符 插入NULL作为尾符 flush:刷新缓冲区,把流从缓冲区输出到目标设备,并清空缓冲区 ws:输入时略去空白字符 dec:令IO数据按十进制格式 hex:令IO数据按十六进制格式 oct:令IO数据按八进制格式
在< iomanip>头文件中定义的操作符有:
setbase(int) resetiosflags(long) setiosflags(long) setfill(char) setprecision(int) setw(int)
这些格式控制符大致可以替代ios的格式函数成员的功能,且使用比较方便。例如,为了把整数345按16进制输出,可以采用两种方式:
int i=345; cout.setf(ios::hex,ios::basefield); cout<<i<<endl;
或者:
cout<<hex<<i<<endl;
可以看出采用格式操纵符比较方便,二者的区别主要在于:格式成员函数是标准输出对象cout的成员函数,因此在使用时必须和cout同时出现,而操纵符是自由函数,可以独立出现,使用格式成员函数要显示采用函数调用的形式,不能用IO运算符”<<”和”>>”形成链式操作。
4、自定义格式操纵符
除了利用系统预定义的操纵符来进行IO格式的控制外,用户还可以自定义操纵符来合并程序中频繁使用的IO读写操作。定义形式如下:输出流自定义操纵符:
ostream &操纵符名(ostream &s) { 自定义代码 return s; }
输入流自定义操纵符:
istream &操纵符名(istream &s{ 自定义代码 return s; }
示例代码如下:
#include <iostream> #include <iomanip> using namespace std; std::ostream& OutputNo(std::ostream& s)//编号格式如:0000001 { s<<std::setw(7)<<std::setfill('0')<<std::setiosflags(std::ios::right); return s; } std::istream& InputHex (std::istream& s)//要求输入的数为十六进制数 { s>>std::hex; return s; } int main() { std::cout<<OutputNo<<8<<std::endl; int a; std::cout<<"请输入十六进制的数:"; std::cin>> InputHex >>a; std::cout<<"转化为十进制数:"<<a<<std::endl; return 0; }
程序中OutputNo和InputHex都是用户自定义的格式操纵符,操作符的函数原型必须满足cout对象的成员函数operator<<()的重载形式
ostream& ostream::operator<<(ostream& (*op)(ostream&));
ostream& ostream::operator<<(ostream& (*op)(ostream&));
二、C++中endl“\n”和‘\n’的区别
“\n”表示搜索一个字符串,只有一个数据是回车符
‘\n’表示一个字符,两者在输出上是一样的!
endl
在c++中,终端输出换行时,用
cout<<......<<endl 与 “\n”都可以,这是初级的认识。但二者有小小的区别,用
endl时会刷新缓冲区,使得栈中的东西刷新一次,但用
“\n”不会刷新,它只会换行,盏内数据没有变化。但一般情况,二者的这点区别是很小的,在大的程序中可能会用到。建议用endl来换行.
endl除了写
’\n’进外,还调用
flush函数,刷新缓冲区,把缓冲区里的数据写入文件或屏幕.考虑效率就用
’\n’.
cout<<endl;除了往输出流中插入一个
\n还有刷新输出流的作用.
cout<<endl;等价于:
cout<<\n<<flush;
在没有必要刷新输出流的时候应尽量使用
cout<<'\n';, 过多的endl是影响程序执行效率低下的因素之一.
相关文章推荐
- 学习笔记【1】——C++中"\n"与endl的区别是什么?
- C++学习之extern "C"
- C++学习之extern "C"
- Android学习20--OpenGL的"mapPoints"
- C++学习手记三:C++操作符重载和内存管理学习要点记录
- 孙鑫C++教程学习记录
- 在Eclipse中运行C++程序出现"Launch failed. Binary not foud"和"Program file not Specified"的问题
- android学习记录(android:id="@+id/example"与android:id="@id/example"区别)
- Python学习——Python中的"is"与“==”
- 把"Essencial C++"读薄(二)
- "基础知识是学习更深入癿技巧 癿必备条件呀"[鸟哥的Linux私房菜]
- 20101230学习记录C++ STL
- Esp8266学习之旅③ 利用 " 软件定时器 "或“硬件定时器” 定时0.5秒闪烁点亮一盏LED。(附带demo)
- Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类
- C++学习记录(20180212)
- 记录 android 开发的一个 "面试" 问题
- C/C++学习笔记20:迷途指针
- C++之"endl"与"\n"的区别
- "晴天"天气app的开源库逐步学习(五)完结篇
- "Python"学习笔记----装饰器