本文主要介绍cout对象的很多方法,这些方法是机器重要的,是cout可以完成输出任务的关键秘诀。
insertion operator
所有重载版本的返回值类型都是ostream &
<<
默认含义是按位左移运算符。但是由于他长得像信息流动,所以就被ostream类发现了,并把它重载了,他就获得了一份新工作,一个新角色。
并且ostream类重载的非常详细,<<
可以识别到C++的所有基本数据类型(14个,11个整型,3个浮点型),即C++给这些数据类型的每一个都提供了一个重载版本。
比如:
cout << 34;//ostream & operator<<(int);
把14中内置基本类型的值都可以转换为文本形式,比如把-2.45转换为5个字符,’-’, ‘2’, ‘.’, ‘4’, ‘5’
char数组名,显式char指针,用引号括起来的C字符串都是字符串指针,因为C++就是用字符串指针来表示字符串的。
除了字符串指针之外的指针,C++就认为是void *
,就会打印地址,即指针变量中存的值,而不是打印字符串了。
如果你不想打印字符串,而是想打印出字符串的地址,那就强制转换为void *
就好啦。
void *
打印字符串的地址#include
int main()
{
using std::cout;
using std::endl;
int eggs = 12;
char * amount = "dozen";
cout << &eggs << endl;//打印地址
cout << amount << endl;//打印字符串
cout << (void *) amount << endl;//打印地址
return 0;
}
0x6efef8
dozen
0x4ba025
ostream &
ostream & put(char);
后来为了用于wchar_t, 把他变成了模板函数,即参数可以是char,可以是wchar_t.
cout.put('i').put('t');
basic_ostream<charT, traits> & write(const char_type * s, streamsize n);//第一个参数是字符串的地址,第二个参数是要显示的字符的数目
#include
#include
int main()
{
using std::cout;
using std::endl;
const char * state1 = "Florida";
const char * state2 = "Kansas";
const char * state3 = "Euphoria";
int len = std::strlen(state2);
cout << "Increasing loop index:\n";
int i;
for (i=1;i<=len;++i){
cout.write(state2, i);
cout << endl;
}
cout << "Decreasing loop index:\n";
for (i=len;i>=1;--i){
cout.write(state2, i);
cout << endl;
}
cout.write(state2, len + 10) << endl;
return 0;
}
state1, state3两个字符串只是为了便于观察长度超出state2的长度时会显示什么。
这几个C字符串都存在一起的诶,地址连着的。
Increasing loop index:
K
Ka
Kan
Kans
Kansa
Kansas
Decreasing loop index:
Kansas
Kansa
Kans
Kan
Ka
K
Kansas Euphoria
cout.write(state2, len + 20) << endl;
输出是:
Kansas Euphoria Index
可以看到,内存中有啥就输出啥了
cout.write(state2, len + 10) << endl;
#include
int main()
{
long val = 560031841;
cout.write((char *) &val, sizeof(long));
return 0;
}
aha!
Process returned 0 (0x0) execution time : 0.575 s
Press any key to continue.
560031841转换为32二进制是(我的计算机上,long占4个字节):
00100001 01100001 01101000 01100001
分别转换为是十进制,对应于:33 97 104 97
把他们当做ascii码,倒着看就是aha!
控制符 manipulator
之前说过了,C++使用缓冲区来匹配不同的传输速率,但是如果输出不是输出到文件,而是输出到屏幕,则其实速率差别并不大,而且我们也绝对不想等到缓冲区的512字节都满了,才全部一起显示到屏幕呀,毕竟我们需要看输出的信息。
所以输出到屏幕的话,刷新输出缓冲区的条件不是缓冲区满,而是使用控制符,C++定义了两个控制符用来强行刷新缓冲区,即把缓冲区的所有数据转移到屏幕:flush和endl。
其实大多数场景都是希望在输入即将发生时输出缓冲区,因为一般输入都需要提前输出对应的输入提示信息。
#include
#include
int main()
{
using std::cout;
using std::endl;
cout << "Hello good-looking! " << std::flush;
cout << "Wait just a moment, please." << endl;
cout << "Hello good-looking! " << std::flush;
return 0;
}
Hello good-looking! Wait just a moment, please.
Hello good-looking!
cout << flush;//相当于flush(cout);
这是因为ostream类对<<
进行了重载,当参数为函数对象时,就调用这个函数并把cout作为参数。
ostream是ios_base类的间接基类(ios_base是ios类的基类,ios类是ostream类的基类),所以ostream类的对象cout可以使用ios_base类的方法。
#include
int main()
{
using std::cout;
cout << "12345678901234567890\n";
char ch = 't';
int t = 123;
cout << ch << ":\n";
cout << t << ":\n";
cout << -t << ":\n";
double f1 = 1.200;
cout << f1 << ":\n";
cout << (f1+1.0/9.0) << ":\n";
double f2 = 1.57e2;
cout << f2 << ":\n";
f2 += 1.0/9.0;
cout << f2 << ":\n";
cout << (f2*1.0e4) << ":\n";
double f3 = 2.3e-4;
cout << f3 << ":\n";
cout << f3/10 << ":\n";
return 0;
}
加个冒号是为了看字段宽度的。
可以看到,1.200末尾的0没有被显示。这是因为,C++默认不显示浮点数结尾的0。
12345678901234567890
t:
123:
-123:
1.2:
1.31111:
157:
157.111:
1.57111e+006:
0.00023:
2.3e-005:
//设置cout对象的计数系统格式状态为十六进制,即以十六进制来打印整数
hex(cout);//相当于cout << hex;
和刚才的flush一样,控制符是函数。但是注意,不是成员函数哈。
#include
int main()
{
using std::cout;
int n = 15;
cout << n << ' ' << n*n << " decimal\n";
//十六进制
cout << std::hex;
cout << n << ' ' << n*n << " hexadecimal\n";
//八进制
cout << std::oct << n << ' ' << n*n << " octal\n";
//回到十进制
dec(cout);
cout << n << ' ' << n*n << " decimal\n";
return 0;
}
15 225 decimal
f e1 hexadecimal
17 341 octal
15 225 decimal
字段宽度是指一个字符串的最大长度,如果设置为10, 则就只能显示最多10个字符,但是如果字符串多于10个字符,C++当然不会给你截短,只显示前10个字符,没那么僵硬,而是会自动增长字段长度,然后完整的显示字符串。
#include
int main()
{
using std::cout;
int w = cout.width(30);
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":\n";
cout << "N * N" << ":\n";
for (long i=1;i<=100;i *= 10){
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i*i << ":\n";
}
return 0;
}
这是因为C++会增长字段,以完整地容纳数据。
用来填充的字符叫做填充字符,fill character, 这里是空格。
default field width = 0:
N:
N * N:
1: 1:
10: 100:
100: 10000:
如果想要左对齐,可以用left控制符
#include
int main()
{
using std::cout;
cout << std::left;
int w = cout.width(30);
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":\n";
cout << "N * N" << ":\n";
for (long i=1;i<=100;i *= 10){
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i*i << ":\n";
}
return 0;
}
default field width = 0:
N :
N * N:
1 :1 :
10 :100 :
100 :10000 :
但是fill()不同于width(), 它一旦设置了,就一直有效。
#include
int main()
{
using std::cout;
int w = cout.width(30);
cout.fill('-');
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":\n";
cout << "N * N" << ":\n";
for (long i=1;i<=100;i *= 10){
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i*i << ":\n";
}
return 0;
}
--------default field width = 0:
----N:
N * N:
----1:-------1:
---10:-----100:
--100:---10000:
#include
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
可以看到,第三行连小数点都不显示了,因为只让显示2位。这里的截短不是C++做的,是程序员自己要截短的,怪不着C++,C++没有私自背地里偷摸摸的给截短。
"Furry Friends" is $20.4!
"Fiery Friends" is $2.78889!
"Furry Friends" is $20!
"Fiery Friends" is $2.8!
要怎么进入科学输出模式和定点输出模式呢??
——使用控制符fixed和scientific!
定点模式
#include
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout << std::fixed;//定点输出模式
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
"Furry Friends" is $20.400000!
"Fiery Friends" is $2.788889!
"Furry Friends" is $20.40!
"Fiery Friends" is $2.79!
科学模式:
#include
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout << std::scientific;//定点输出模式
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
"Furry Friends" is $2.040000e+001!
"Fiery Friends" is $2.788889e+000!
"Furry Friends" is $2.04e+001!
"Fiery Friends" is $2.79e+000!
从上面的示例可以看到,C++默认是不会显示末尾的0,如果精度太少,则也不会显示小数点。
但是有时候,比如显示账目,把小数点和0都显示出来更加美观。但是iostream类没有提供相关方法,ios_base类提供了。
注意showpoint是ios_base类的类级静态常量,即其具有类作用域,所以如果在类的成员方法外面用它,则要用作用域运算符说明ios_base类。
#include
int main()
{
using std::cout;
using std::ios_base;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout.setf(ios_base::showpoint);//显示小数点,且显示末尾0
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
20已经占用了2位,所以小数点显示了就不会再显示后面的了。而且可以看到,小数点并没有占精度位。
"Furry Friends" is $20.4000!
"Fiery Friends" is $2.78889!
"Furry Friends" is $20.!
"Fiery Friends" is $2.8!
setf()是ios_base类的一个成员函数
ios_base类有一个受保护的数据成员,它的每一位,都表示和控制着输出格式化的一个方面。比如有3个bit表示计数系统,hex,dec, oct。
除了上面展示的用控制符来改变输出格式状态外,还可以用setf()方法。
setf()有两种重载版本。
第一种:
fmtflags setf(fmtflags);//用于设置单个位控制的格式信息,返回的是设置之前的信息
fmtflags是bitmask类的typedef名称,用来存储格式标记,在ios_base类中定义。bitmask类的关键思想是每一位都可以单独访问,且都有自己的含义。iostream包使用bitmask来存储状态信息。bitmask类型可以是整型,枚举或者STL的bitset容器。
fmtflags常量已经被ios_base类定义了, 我们不需要记住数字,直接传入这些常量就好啦,但是要知道他们实际上就是bitmask类型的值哦,即fmtflags的值。
#include
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
int temperature = 63;
cout << "Today's water temperature:";
cout.setf(ios_base::showpos);//在十进制正数前面显示+符号
cout << temperature << endl;
cout << "For our programming friends, that's ";
cout << std::hex << temperature << endl;
cout.setf(ios_base::uppercase);//对16禁进制输出,用大写字母(只针对16进制)
cout.setf(ios_base::showbase);//显示基数前缀0x
cout << "or\n";
cout << temperature << endl;
cout << "How " << true << "! oops -- How ";
cout.setf(ios_base::boolalpha);//显示true或者false
cout << true << "!\n";
return 0;
}
Today's water temperature:+63
For our programming friends, that's
3f
or
0X3F
How 0X1! oops -- How true!
学到的点:
fmtflags setf(fmtflags, fmtflags);//返回的也是设置之前的信息
第一个参数用来表示要设置哪些位(比如floatfield则设置科学位和定点位这两个比特,其他位置完全不影响),和第一个原型的那个参数的功能一样;第二个参数用来表示要清除哪些位,比如你要把进制从十进制改为16进制,则要把hex那一位改为1,还要把dec那一位改为0呀。
而且由于ios_base类已经定义好了常量,就特别方便。
左对齐:把值放在字段的左端。
内部对齐:把符号或者基数前缀放在字段左端,而数字放在字段右边。
示例
#include
#include
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
cout.setf(ios_base::left, ios_base::adjustfield);//左对齐left justification
cout.setf(ios_base::showpos);//显示正数前面的符号
cout.setf(ios_base::showpoint);//显示小数点
cout.precision(3);
//使用科学模式
ios_base::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield);//存住旧的存储格式
long n;
cout << "左对齐\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << " \n";
}
cout.setf(ios_base::internal, ios_base::adjustfield);
cout.setf(old, ios_base::floatfield);
cout << "内部对齐\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout << "右对齐\n";
cout.setf(ios_base::right, ios_base::adjustfield);
cout.setf(ios_base::fixed, ios_base::floatfield);
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
return 0;
}
左对齐
+1 |+1.000e+000
+11 |+3.317e+000
+21 |+4.583e+000
+31 |+5.568e+000
+41 |+6.403e+000
内部对齐
+ 1|+ 1.00|
+ 11|+ 3.32|
+ 21|+ 4.58|
+ 31|+ 5.57|
+ 41|+ 6.40|
右对齐
+1| +1.000|
+11| +3.317|
+21| +4.583|
+31| +5.568|
+41| +6.403|
学到的点:
可以用下面这句代码设置为默认模式,注意要强制转换,不然会报错,说不能把int转换为fmtflags类型:
cout.setf((ios_base::fmtflags)0, ios_base::floatfield);//第一个参数表示不设置任何位(科学位和定点位都不设置)
#include
#include
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
cout.setf(ios_base::left, ios_base::adjustfield);//左对齐left justification
cout.setf(ios_base::showpos);//显示正数前面的符号
cout.setf(ios_base::showpoint);//显示小数点
cout.precision(3);
//使用科学模式
ios_base::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield);//存住旧的存储格式
long n;
cout << "左对齐(科学模式)\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << " \n";
}
cout.setf(ios_base::internal, ios_base::adjustfield);
cout.setf(old, ios_base::floatfield);
cout << "内部对齐(默认模式)\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout << "右对齐(定点模式)\n";
cout.setf(ios_base::right, ios_base::adjustfield);
cout.setf(ios_base::fixed, ios_base::floatfield);
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
//使用默认模式
cout << "右对齐(默认模式)\n";
cout.setf((ios_base::fmtflags)0, ios_base::floatfield);
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
return 0;
}
左对齐(科学模式)
+1 |+1.000e+000
+11 |+3.317e+000
+21 |+4.583e+000
+31 |+5.568e+000
+41 |+6.403e+000
内部对齐(默认模式)
+ 1|+ 1.00|
+ 11|+ 3.32|
+ 21|+ 4.58|
+ 31|+ 5.57|
+ 41|+ 6.40|
右对齐(定点模式)
+1| +1.000|
+11| +3.317|
+21| +4.583|
+31| +5.568|
+41| +6.403|
右对齐(默认模式)
+1| +1.00|
+11| +3.32|
+21| +4.58|
+31| +5.57|
+41| +6.40|
示例:
#include
int main()
{
using std::cout;
using std::ios_base;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout.setf(ios_base::showpoint);//显示小数点,且显示末尾0
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.unsetf(ios_base::showpoint);
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
"Furry Friends" is $20.4000!
"Fiery Friends" is $2.78889!
"Furry Friends" is $20.!
"Fiery Friends" is $2.8!
"Furry Friends" is $20!
"Fiery Friends" is $2.8!
另一个示例:
#include
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
int temperature = 63;
cout << "Today's water temperature:";
cout.setf(ios_base::showpos);//在十进制正数前面显示+符号
cout << temperature << endl;
cout << "For our programming friends, that's ";
cout << std::hex << temperature << endl;
cout.setf(ios_base::uppercase);//对16禁进制输出,用大写字母(只针对16进制)
cout.setf(ios_base::showbase);//显示基数前缀0x
cout << "or\n";
cout << temperature << endl;
cout << "How " << true << "! oops -- How ";
cout.setf(ios_base::boolalpha);//显示true或者false
cout << true << "!\n";
cout.unsetf(ios_base::boolalpha);//不显示true或者false
cout << true << "!\n";
return 0;
}
Today's water temperature:+63
For our programming friends, that's 3f
or
0X3F
How 0X1! oops -- How true!
0X1!
unsetf(ios_base::field);
都会切换回默认模式。setf()成员方法可以设置格式,但是需要提供各种常量参数,不是很方便,C++为了用户控制格式更方便,就提供了很多控制符。比如dec, hex, oct
cout << dec << left << fixed;//使用十进制计数,显示左对齐,定点模式输出浮点数
这张表的右边是用setf()或者unsetf()方法,可以看到使用控制符真的方便很多,打字都少了好多。
这都要归功于重载的插入运算符哇。
io manipulator 输入输出控制符
前面的标准控制符不能设置字段宽度,所以iomanip头文件又加了一些控制符,但是这些控制符和标准控制符不一样,=他们带参数,但是他们不是成员函数,仍然是控制符,所以还是用插入运算符使用它们!!
setprecision()
:设置精度,接受一个指定精度的整数参数setfill()
:接受一个指定填充字符的char参数setw()
:接受一个指定字段宽度的整数参数#include
#include
#include
int main(){
using namespace std;
cout << fixed << right;
cout << setw(6) << "N" << setw(14) << "square root"
<< setw(15) << "fourth root\n";
double root;
for (int n=10;n<=100;n+=10){
root = sqrt(double(n));
cout << setw(6) << setfill('.') << n << setfill(' ')
<< setw(12) << setprecision(3) << root
<< setw(14) << setprecision(4) << sqrt(root)
<< endl;
}
return 0;
}
N square root fourth root
....10 3.162 1.7783
....20 4.472 2.1147
....30 5.477 2.3403
....40 6.325 2.5149
....50 7.071 2.6591
....60 7.746 2.7832
....70 8.367 2.8925
....80 8.944 2.9907
....90 9.487 3.0801
...100 10.000 3.1623
cout << 1.25
就会调用ostream & operator<<(double)
原型,而这个函数的功能就是把double浮点数转换为文本,在这里就是转换为四个字符。