要使用来自传统 iostream 库的例程,必须针对所需的库部分将头文件包括进来。下表对头文件进行了具体描述。
表 14–1 iostream 例程头文件
头文件 |
说明 |
---|---|
声明 iostream 库的基本功能。 |
|
声明专用于文件的 iostream 和 streambuf。包括了 iostream.h。 |
|
声明字符数组专用的 iostream 和 streambuf。包括了 iostream.h。 |
|
(已过时)声明专用于 stdio 文件的 iostream 和 streambuf。包括了 iostream.h。 |
|
(已过时)包括了 iostream.h、fstream.h、iomanip.h 和 stdiostream.h。用于兼容 C++ 1.2 版的旧式流。 |
通常程序中不需要所有这些头文件,而仅包括所需声明的头文件。在兼容模式 (-compat[=4]) 下,传统 iostream 库是 libC 的一部分,它由 CC 驱动程序自动链接。在标准模式(缺省模式)下, libiostream 包含传统 iostream 库。
使用 iostream 进行的输出通常依赖于重载的左移运算符 (<<)(在 iostream 上下文中,称为插入运算符)。要将值输出到标准输出,应将值插入预定义的输出流 cout 中。例如,要将给定值 someValue 发送到标准输出,可以使用以下语句:
cout << someValue; |
对于所有内置类型,都会重载插入运算符,且 someValue 表示的值转换为其适当的输出表示形式。例如,如果 someValue 是 float 值,则 << 运算符会将该值转换为带小数点的适当数字序列。此处是将 float 值插入输出流中,因此 << 称为浮点插入器。通常,如果给定类型 X,则 << 称为 X 插入器。ios(3CC4) 手册页中讨论了输出格式以及如何控制输出格式。
iostream 库不支持用户定义类型。如果定义要以自己的方式输出的类型,必须定义插入器(即重载 << 运算符)来正确处理它们。
<< 可以多次应用。要将两个值插入 cout 中,可以使用类似于以下示例中的语句:
cout << someValue << anotherValue; |
以上示例的输出在两个值间不显示空格。因此您可能会要按以下方式编写编码:
cout << someValue << " " << anotherValue; |
运算符 << 优先作为左移运算符(其内置含义)使用。与其他运算符一样,总是可以使用圆括号来指定操作顺序。使用括号来避免优先级问题是个很好的方法。下列四个语句中,前两个是等价的,但后两个不是。
cout << a+b; // + has higher precedence than << cout << (a+b); cout << (a&y); // << has precedence higher than & cout << a&y; // probably an error: (cout << a) & y |
以下示例定义了 string 类:
#include <stdlib.h> #include <iostream.h> class string { private: char* data; size_t size; public: // (functions not relevant here) friend ostream& operator<<(ostream&, const string&); friend istream& operator>>(istream&, string&); }; |
在此示例中,必须将插入运算符和提取运算符定义为友元,因为 string 类的数据部分是 private。
ostream& operator<< (ostream& ostr, const string& output) { return ostr << output.data;} |
以下是为要用于 string 而重载的 operator<< 的定义。
cout << string1 << string2; |
operator<< 将 ostream&(也就是对 ostream 的引用)作为其第一个参数,并返回相同的 ostream,这样就可以在一个语句中合并多个插入。
通常情况下,重载 operator<< 时不必检查错误,因为有 iostream 库传播错误。
出现错误时,所在的 iostream 会进入错误状态。iostream 状态中的各个位根据错误的一般类别进行设置。iostream 中定义的插入器不会尝试将数据插入处于错误状态的任何流,因此这种尝试不会更改 iostream 的状态。
通常,处理错误的推荐方法是定期检查某些中心位置中输出流的状态。如果有错误,就应该以某些方式来处理。本章假定您定义了函数 error,该函数可采用字符串并中止程序。error 不是预定义的函数。有关 error 函数的示例,请参见14.3.9 处理输入错误。可以使用运算符 ! 检查 iostream 的状态,如果 iostream 处于错误状态,该运算符会返回非零值。例如:
if (!cout) error("output error"); |
还有另外一种方法来测试错误。ios 类可定义 operator void *(),它在有错误时返回空指针。您可以使用如下语句:
if (cout << x) return; // return if successful |
也可以使用函数 good,它是 ios 的成员:
if (cout.good()) return; // return if successful |
enum io_state {goodbit=0, eofbit=1, failbit=2, badbit=4, hardfail=0x80}; |
有关错误函数的详细信息,请参见 iostream 手册页。
与大多数 I/O 库一样,iostream 通常会累积输出并将其发送到较大且效率通常较高的块中。如果要刷新缓冲区,只要插入特殊值 flush。例如:
cout << "This needs to get out immediately." << flush; |
flush 是一种称为操纵符的对象示例,它是一个值,可以插入 iostream 中以起到一定作用,而不是使输出其值。它实际上是一个函数,采用 ostream& 或 istream& 参数,在对其执行某些操作后返回其参数(请参见14.7 操纵符)。
要获得原始二进制形式的值输出,请使用以下示例所示的成员函数 write。该示例显示了原始二进制形式的 x 输出。
cout.write((char*)&x, sizeof(x)); |
以上示例将 &x 转换为 char*,这违反了类型规程。一般情况下,这样做无关大碍,但如果 x 的类型是具有指针、虚拟成员函数的类或是需要 nontrivial 构造函数操作的类,就无法正确读回以上示例写入的值。
使用 iostream 进行的输入类似于输出。需要使用提取运算符 >>,可以像插入操作那样将提取操作串接在一起。例如:
cin >> a >> b; |
该语句从标准输入获得两个值。与其他重载运算符一样,所用的提取器依赖于 a 和 b 的类型(如果 a 和 b 的类型不同,则使用两个不同的提取器。)ios(3CC4) 手册页中详细讨论了输入格式以及如何控制输入格式。通常,前导空白字符(空格、换行符、标签、换页等)被忽略。
要输入新的类型时,如同重载输出的插入运算符,请重载输入的提取运算符。
类 string 定义了其提取运算符,如以下代码示例所示:
istream& operator>> (istream& istr, string& input) { const int maxline = 256; char holder[maxline]; istr.get(holder, maxline, ”\n’); input = holder; return istr; } |
get 函数从输入流 istr 读取字符,并将其存储在 holder 中,直到读取了 maxline-1 字符、遇到新行或 EOF(无论先发生哪一项)。然后,holder 中的数据以空终止。最后,holder 中的字符复制到目标字符串。
按照约定,提取器转换其第一个参数中的字符(此示例中是 istream& istr),将其存储在第二个参数(始终是引用),然后返回第一个参数。因为提取器会将输入值存储在第二个参数中,所以第二个参数必须是引用。
此处提及这个预定义的提取器是因为它可能产生问题。使用方法如下:
char x[50]; cin >> x; |
该提取器跳过前导空白,提取字符并将其复制到 x 中,直至遇到另一个空白字符。最后完成具有终止空 (0) 字符的字符串。因为输入会溢出给定的数组,所以要小心操作。
您还必须确保指针指向了分配的存储。例如,下面列出了一个常见的错误:
char * p; // not initialized cin >> p; |
因为没有告知存储输入数据的位置,所以会导致程序的终止。
除了使用 char 提取器外,还可以使用任一形式的 get 成员函数获取一个字符。例如:
char c; cin.get(c); // leaves c unchanged if input fails int b; b = cin.get(); // sets b to EOF if input fails |
以下方法可以只跳过空格,并在制表符、换行符或任何其他字符处停止:
int a; do { a = cin.get(); } while(a ==’ ’); |
如果需要读取二进制值(如使用成员函数 write 写入的值),可以使用 read 成员函数。以下示例说明了如何使用 read 成员函数输入原始二进制形式的 x,这是先前使用 write 的示例的反向操作。
cin.read((char*)&x, sizeof(x)); |
可以使用 peek 成员函数查看流中的下一个字符,而不必提取该字符。例如:
if (cin.peek()!= c) return 0; |
缺省情况下,iostream 提取器会跳过前导空白。可以关闭 跳过标志防止这种情况发生。以下示例先关闭了 cin 跳过空白功能,然后将其打开:
cin.unsetf(ios::skipws); // turn off whitespace skipping ... cin.setf(ios::skipws); // turn it on again |
可以使用 iostream 操纵符 ws 从 iostream 中删除前导空白,而不论是否启用了跳过功能。以下示例说明了如何从 iostream istr 中删除前导空白:
istr >> ws; |
按照约定,第一个参数为非零错误状态的提取器不能从输入流提取任何数据,且不能清除任何错误位。失败的提取器至少应该设置一个错误位。
对于输出错误,您应该定期检查错误状态,并在发现非零状态时采取某些操作(诸如终止)。! 运算符测试 iostream 的错误状态。例如,如果输入字母字符用于输入,以下代码就会产生输入错误:
#include <stdlib.h> #include <iostream.h> void error (const char* message) { cerr << message << "\n"; exit(1); } int main() { cout << "Enter some characters: "; int bad; cin >> bad; if (!cin) error("aborted due to input error"); cout << "If you see this, not an error." << "\n"; return 0; } |
类 ios 具有可用于错误处理的成员函数。详细信息请参见手册页。
可以将 stdio 用于 C++ 程序,但在程序内的同一标准流中混合使用 iostream 与 stdio,会发生问题。例如,如果同时向 stdout 和 cout 写入,会发生独立缓冲,并产生不可预料的结果。如果从 stdin 与 cin 两者进行输入,问题会更加严重,因为独立缓冲可能会破坏输入。
要消除标准输入、标准输出和标准错误中的这种问题,就请在执行输入或输出前使用以下指令:它将所有预定义的 iostream 与相应预定义的 stdio 文件连接起来。
ios::sync_with_stdio(); |
因为在预定义流作为连接的一部分成为无缓冲流时,性能会显著下降,所以该连接不是缺省连接。可以在应用于不同文件的同一程序中同时使用 stdio 和 iostream。也就是说,可以使用 stdio 例程向 stdout 写入,并向连接到 iostream 的其他文件写入。可以打开 stdio 文件进行输入,并从 cin 读取,只要不尝试从 stdin 读取即可。