#include <iostream.h> #include <iomanip.h> #define IOMANIPdeclare(typ) expands to the following definitions ... class SMANIP(typ) { public: SMANIP(typ)(ios& (*)(ios&, typ), typ); friend istream& operator>>(istream&, SMANIP(typ)&); friend ostream& operator<<(ostream&, SMANIP(typ)&); }; class SAPP(typ) { public: SAPP(typ)(ios& (*)(ios&, typ)); SMANIP(typ) operator()(typ); }; class IMANIP(typ) { public: IMANIP(typ)(istream& (*)(istream&, typ), typ); friend istream& operator>>(istream&, IMANIP(typ)&); }; class IAPP(typ) { public: IAPP(typ)(istream& (*)(istream&, typ)); IMANIP(typ) operator()(typ); }; class OMANIP(typ) { public: OMANIP(typ)(ostream& (*)(ostream&, typ), typ); friend ostream& operator<<(ostream&, OMANIP(typ)&); }; class OAPP(typ) { public: OAPP(typ)(ostream& (*)(ostream&, typ)); OMANIP(typ) operator()(typ); }; class IOMANIP(typ) { public: IOMANIP(typ)(iostream& (*)(iostream&, typ), typ); friend istream& operator>>(iostream&, IOMANIP(typ)&); friend ostream& operator<<(iostream&, IOMANIP(typ)&); }; class IOAPP(typ) { public: IOAPP(typ)(iostream& (*)(iostream&, typ)); IOMANIP(typ) operator()(typ); }; IOMANIPdeclare(int); IOMANIPdeclare(long); smanip_long resetiosflags(long); smanip_int setbase(int); smanip_int setfill(int); smanip_long setiosflags(long); smanip_int setprecision(int); smanip_int setw(int);
操纵符好像是插入或提取到流中的对象,但它们中的许多仅更改流的状态。它们的用途是将实际上是函数调用的东西嵌入到插入或提取序列中以提供便利。例如,我们可以不编写以下代码:
cout.width(8); cout << val1 << " "; cout.width(4); cout << val2 << '\n'; cout.flush();
而编写以下代码:
cout << setw(8) << val1 << " " << setw(4) << val2 << endl;
ios(3CC4)、istream(3CC4) 和 ostream(3CC4) 中描述了多个预定义的操纵符。这些非常简单,不接受参数,例如上面使用的 endl 或者 flush。操纵符还可以接受参数,例如上面使用的 setw 或者 setfill。头文件 <iomanip.h> 定义了这些操纵符中的一些,并且还提供了创建您自己的操纵符所需的定义。
操纵符在逻辑上定义为模板,但其引入时间要早于 C++ 语言中提供模板的时间。相应地,它们定义为模拟模板的宏。宏 IOMANIPdeclare(typ) 展开为接受类型为 typ 的一个参数的操纵符的一组完整定义。由于宏的本质,类型参数必须是一个简单的类型名称(只是一个标识符)。头文件提供了类型 int 和 long 的展开的定义。
没有参数的操纵符是具有一个类型为流引用的参数的函数,该函数返回同一类型。流具有预定义的重载运算符,这些运算符接受此类函数作为参数。操纵符对流参数执行必需的操作,然后返回同一个流。例如,下面是针对 tab 操纵符的代码:
ostream& tab(ostream& s) { s << '\t'; return s; }
在表达式 cout<<tab 中,选择了重载的运算符
ostream& operator<< (ostream& (*)(ostream&))
它只调用操纵符函数。得到的调用会将一个 tab 字符插入到 ostream 中。
具有参数的操纵符有两个部分:
manip 部分:一个接受流和 typ 参数并返回流的函数;
apply 部分:manip 部分调用的函数,该函数应用状态更改或其他操作。
对于给定的类型 typ,所有部分都是由 IOMANIPdeclare 宏声明的。下文中将对它们进行描述。
在下面的论述中,假设了以下声明:
typ - some type name n - an int l - a long s - an ios i - an istream o - an ostream io - an iostream f - an ios& (*) (ios&) if - an istream& (*) (istream&) of - an ostream& (*) (ostream&) iof - an iostream& (*) (iostream&)
s << SMANIP(typ)(f, t) s << SAPP(typ)(f)(t) s >> SMANIP(typ)(f, t) s >> SAPP(typ)(f)(t)
返回 f(s,t)。流 s 还可以是其他流类型 i、o 和 io 中的任何一个。
i >> IMANIP(typ)(if, t) i >> IAPP(typ)(if)(t)
返回 if(i,t)。
o << OMANIP(typ)(of, t) o << OAPP(typ)(of)(t)
返回 of(o,t)。
io << IOMANIP(typ)(iof, t) io << IOAPP(typ)(iof)(t) io >> IOMANIP(typ)(iof, t) io >> IOAPP(typ)(iof)(t)
返回 iof(io,t)。
<iomanip.h> 头文件包含接受 int 或 long 参数(在上面的演示中为 typ)的操纵符的声明。预定义的操纵符都用于更改流的状态,如下所述:
将流 i 或 o 的字段宽度设置为 n。注意:下一次有格式的插入或提取会将宽度重置为 0。
将流 i 或 o 的填充字符设置为 n。
将流 i 或 o 的精度变量设置为 n。
开启流 i 或 o 的 ios 标志,这些标志是在 l 中设置的。
关闭流 i 或 o 的 ios 标志,这些标志是在 l 中设置的。
要编写您自己的参数化操纵符,您需要声明 manip 函数,然后实现 manip 和 apply 函数。如果操纵符接受 typ 类型的参数,请将 manip 函数声明为接受该类型的一个参数并返回 Xmanip_typ 类型的对象。如果打算将其用于操纵 ios、istream、ostream 或 iostream 类型的对象,请分别将 X 替换为 s、i、o 或 io 中的一个。将 typ 替换为实际的类型名称。对于类型 int 和 long,所有声明都已就位。对于其他类型,您需要调用“模板”IOMANIPdeclare(typ)。
apply 函数通常是静态的,因为只有 manip 函数会调用它。将它定义为返回对要操纵的流类型的引用,具有该同一类型的第一个参数和 typ 类型的第二个参数。
例如,请考虑 setiosflags 操纵符。该操纵符对 ios 进行操作并接受一个 long 参数,因此它被声明为返回 smanip_long 类型。如果它对 ostream 进行操作并接受一个 int 参数,则它将被声明为返回 omanip_int 类型。因此,将在头中声明操纵符(manip 函数),如下所示:
smanip_long setiosflags(long);
apply 函数未出现在头中,因为它从不直接通过用户代码进行调用。两个函数的代码都出现在实现模块中:
// set the flags bitvector according to the bits set in b static ios& sios(ios& i, long b) // private apply function { i.setf(b); return i; } smanip_long setiosflags(long b) // public manip function { return smanip_long(sios, b); }
ios (3CC4) 、 ios.intro (3CC4) 、 istream (3CC4) 、 ostream (3CC4)
《C++ Library Reference》中的第 3 章 "The Classic iostream Library"