8.3 自定义的操纵算子
8.3.1 不带参数的操纵算子
定义不带参数的操纵算子比较简单。下面的程序定义了一个名为newline的操纵算子,它和预定义的操纵算子endl具有相同的功能。
例8_9:定义一个名为newline的操纵算子EX8_9.CPP
操纵算子的目的主要是解决在流的插入或提出表达式中使用函数调用所引起的问题。
一个简单的操纵处算符是一个说明为
type& fun(type&);
的函数地址。这里type可以是istream、ostream、iostream或ios类的类名。
在istream类中说明了两个特殊的插入函数:
istream& istream::operator>>(istream& (*anFun)(istream&));
isteam& istream::operator>>(ios& (*anFun)(ios&));
在ostream类中也说明了两个特殊的插入函数:
ostream& ostream::operator>>(ostream& (*anFun)(ostream&));
osteam& ostream::operator>>(ios& (*anFun)(ios&));
这些成员函数都接收一个函数指针参数。当这些成员函数被启动时,它们又简单地通过这个函数指针调用这个指针所指向的函数。
语句 cout<<”A line”<<newline<<”another line”; 计算过程:
首先计算优先级比运算符<<高的运算符组成的表达式,然后对表达式
cout<<”A line”
使用插入操作符ostream::operator<<(char *),最后,表达式
cout<<newline
使用上面列出的ostream类的第一个插入函数调用newline。
8.3.2 带有一个int或long类型参数的操纵算子
由于函数调用运算符的优先级高于插入符,所以语句 cout<<a<<setprecsion(2)<<b;中先调用函数setprecision。但是,函数setprecision必须在插入符输出了变量a之后才能设置精度,因此在调用函数setprecision时不能立即设置精度,相反,它应返回某个对象,这个对象可被它所在的类中重载的插入符用来为变量b设置所需的精度。
C++系统提供的头文件iomanip.h中定义了一系列的宏,来帮助程序员定义带有一个参数的操纵算子。这个头文件中还特别定义了帮助程序员定义一个带有int类型或long类型参数的操纵算子所需的类。在该文件中,setprecision被定义为:
SMANIP(int) setprecision(int n);
函数setprecision()返回SMANIP(int)类的一个对象,SMANIP(int)类的构造函数在这个对象中保存下来一个函数的地址和它的参数值。所以语句 (cout<<a<<setprecsion(2)<<b;)的计算过程是:
1、首先计算表达式a、b和setprecision的值,函数setprecision返回一个SMANIP(int)类的一个对象。
2、使用插入符ostream::operator<< (float)将表达式a的值以原有的精度输出。
3、使用插入符operator<<(ios&,SMANIP(int))访问setprecision所返回的对象,使用这个对象中保存的函数setprecision的地址和参数2设置精度。
4、使用ostream::operator<< (float)按新设置的精度输出表达式b的值。
例8_10:设计一个带有一个int类型参数的操纵算子star(EX8_10.CPP)。
OMANIP(int)是iomanip.h中定义的一个类名。第二个star函数仅有的功能是使用OMANIP(int)类的构造函数:
OMANIP(int)::OMANIP(int)(ostream& (*anFun)(ostream&,int),int);
建立一个OMANIP(int)的一个对象,并返回这个对象。由于OMANIP(int)的构造函数的第一个参数的函数原型为,ostream& fun(ostream&,int);
所以在return语句中使用了第一个star函数的地址。
为了便于以后叙述,将建立一个对象的函数称为操纵算子。如上例的函数
OMANIP(int) star(int);
而将完成操纵算子的功能的函数称为操纵函数,如上例中的函数
ostream& star(ostream&,int);
操纵算子和操纵函数协同完成对流所需的操作。
在iomanip.h中,针对ios、istream、ostream和iostream类分别预定义了SMANIP(int)、IMANIP(int)、OMANIP(int)和IOMANIP(int)类,以简化程序员定义带有一个int类型参数的操纵算子的工作。这些类分别用于需要ios、istream、ostream和iostream类作为第一个参数类型的操纵函数中,但更主要地取决于在操纵算子中准备使用哪个类中的成员函数。在iomanip.h中也同样预定义了一个SMANIP(long)、IMANIP(long)、OMANIP(long)和IOMANIP(long)类,供程序员用来定义带有一个long类型参数的操纵算子。
8.3.3 定义任意参数类型的操纵算子
我们为complex类的对象定义一个setformat操纵算子,它用于设置所有complex类的对象输出(或输入)时的域宽、精度和所使用的分隔符。设有complex类的两个对象c1和c2,它们分别表示复数5+3i和 4+2i,下面是使用操纵算子setformat的一个语句示例:
例8_11:定义任意参数类型的操纵算子示例之一(EX8_11.CPP)。
例8_12:定义任意参数类型的操纵算子示例之二(EX8_12.CPP)。