2015年(29)
分类: C/C++
2015-04-17 08:26:00
cout在c++中未ostream类型,也就是说,cout是要个类,但是ostream类型有点特别,因为它的构造函数是保护成员,也就是说,您不能够声明一个对象为ostream类型。但是,在std空间中提供了一个ostream全局变量cout供编程人员使用。那既然构造函数是保护成员,那怎么能产生示例呢?您可以去看看单例模式,那里会给您答案,因为本篇不是讲解设计模式,对于单例模式的解释暂且略过。
一、cout的使用
cout 相对于c中的printf,有它独特的地方,printf在输出内容时,如果内容的类型、个数不一定,需要在格式化字符串中指明类型,而cout就不需要,好像什么类型的数据给他,都会进行正确的解析,是不是很神奇。当然,这些神奇都是人为实现的,如果您定义了一个自己的类型,比如struct A吧,您认为cout神通广大,可以用cout输出A类型变量试试,一定会编译错误,很明显,cout不认识类型A,那如何让cout能够认识A呢?用一种在c++中特殊的技术,叫做运算符重载。
重载:函数或方法(其实是一个意思,通常类中的函数叫做方法,类中的变量也会叫做属性)有相同的名称,但是,参数列表不相同的情形,这种同名不同参的函数或方法之间,相互称为重载函数或方法。
重载特性:重载函数必须在同一命名空间中,你说我在空间A中定义了fun,又在B中定义了fun,它们参数不同,能否构成重载?NO,c++编译器为了实现重载功能,会对函数名进行修饰,其实,最后的函数已经可以说被修饰的面目全非了,这个被修饰后得到的名称叫做函数签名(Function Signature),在程序中,函数名可以相同,但是函数签名一定不能相同,否则会出现重定义,而C语言中没有这种名字修饰的机制,所以C语言是不支持重载滴。
函数签名分为两部分,即“函数名+参数列表”,其中函数名部分相同,参数列表不同的两个函数才能成为重载函数,而函数名包括命名空间的名称,所以如果两个函数的命名空间都不同,那么可以说函数名部分就不相同了,根本无所谓重载,另外,还能解释为什么如果函数名相同、参数系统,但是返回值不同不能构成重载,因为函数签名中根本没有返回值,那在这种情况下,函数签名一定是相同的,所以提示重定义错误了。
那说了这么多,到底怎么对cout的“<<”运算符进行重载呢?店小二,上代码:
struct A
{
int m_value;
char m_name[10];
};
ostream& operator<<(ostream& o,const A& a)
{
return o << "A(" << a.m_value << ',' << a.m_name << ')';
}
这就是对运算符 << 的重载。调用代码如下:
A a = {12,"jame"};
cout << a << endl;
输出结果为:A(12,jame)
关于运算符的重载,下一篇在给各位看官慢慢说来。
二、cout的格式化输出
在C语言中,在格式化字符串中,“%x”代表16进制输出,”%o”代表8进制输出,“%d”代表十进制输出,那cout中如何控制输出格式呢?
在C++中,有一个标志类型ostream::fmtflags用于记录cout的当前格式。
大家如果在改变格式并完成一定的操作,想恢复原格式,可以先将该变量存下来,之后恢复。
如下:
ostream::fmtflags fs = cout.flags(); //获得原始flags
cout << oct; //设置以8进制输出
cout << 13 << endl;
cout << "------" << endl;
cout.flags(fs); //恢复原始格式
cout << 13 << endl;
当然”cout << oct”; 等价于”oct(cout)”;
为啥会这样呢?您可以用cout << typedef(oct).name() << endl;查看一下oct的类型,其中typedef为
我的理解是,如果调用oct(cout)的形式改变cout的flag,这是理所当然的,因为oct本来就是函数嘛,那为什么用运算符 << 也能达到相同的效果呢?应该是在cout的操作符 << 的重载函数中包含了动态类型转换dynamic_cast。以下内容为本人猜测:
oct、hex、dec都为函数,他们的类型都为 void fun(ostream& o);那么定义一个这种函数的类型“typedef fun(ostream& o);”然后通过重载<<操作符,“ostream&
operator<<(ostream&o, void*
input_fun)”,在函数体中,通过dynamic_cast
当然,实际情况比这个复杂。
其他的格式如下:
调整字段宽度:cout.width(10);
恢复默认宽度:cout.width();
设置填充字符:cout.fill(‘0’);
设置浮点数显示精度:cout.precision(2);
恢复默认角度:cout.precision();
设置左对齐:cout.flag(ios::left);
设置右对齐:cout.flag(ios::right);