跳过导航链接 | |
退出打印视图 | |
![]() |
Oracle Solaris Studio 12.3:C++ 用户指南 Oracle Solaris Studio 12.3 Information Library (简体中文) |
要使用来自传统 iostream 库的例程,必须针对所需的库部分将头文件包括进来。下表对头文件进行了具体描述。
表 13-1 iostream 例程头文件
|
通常程序中不需要所有这些头文件,而仅包括所需声明的头文件。缺省情况下,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;
运算符 << 将 ostream&(也就是对 ostream 的引用)作为其第一个参数,并返回相同的 ostream,这样就可以在一个语句中合并多个插入。
通常情况下,重载 operator<< 时不必检查错误,因为有 iostream 库传播错误。
出现错误时,所在的 iostream 会进入错误状态。iostream 状态中的各个位根据错误的一般类别进行设置。iostream 中定义的插入器不会尝试将数据插入处于错误状态的任何流,因此这种尝试不会更改 iostream 的状态。
通常,处理错误的推荐方法是定期检查某些中心位置中输出流的状态。如果存在错误,则应该以某种方式进行处理。本章假定您定义了函数 error,该函数接受字符串并中止程序。error 不是一个预定义函数。有关 error 函数的示例,请参见13.3.9 处理输入错误。您可以使用 运算符 ! 来检查 iostream 的状态,如果 iostream 处于错误状态,则会返回非零值。例如:
if (!cout) error("output error");
还有另外一种方法来测试错误。ios 类定义了 operator void *(),因此当发生错误时,它将返回 NULL 指针。您可以使用如下例所示的语句:
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& 参数,在对其执行某些操作后返回其参数(请参见13.7 操纵符)。
要获得原始二进制形式的值输出,请使用以下示例所示的成员函数 write。该示例显示了原始二进制形式的 x 输出。
cout.write((char*)&x, sizeof(x));
以上示例将 &x 转换为 char*,这违反了类型规程。一般情况下,这样做无关大碍,但如果 x 的类型是具有指针、虚拟成员函数的类或是需要 nontrivial 构造函数操作的类,就无法正确读回以上示例写入的值。
使用 iostream 进行的输入类似于输出。需要使用提取运算符 >>,可以像插入操作那样将提取操作串接在一起。例如:
cin >> a >> b;
该语句从标准输入获得两个值。与其他重载的运算符一样,所使用的提取器取决于 a 和 b 的类型。如果 a 和 b 具有不同的类型,则会使用两个不同的提取器。ios(3CC4) 手册页中详细讨论了输入格式以及如何控制输入格式。通常,前导空白字符(空格、换行符、标签、换页等)被忽略。
要输入新的类型时,如同重载输出的插入运算符,请重载输入的提取运算符。
类 string 定义了其提取运算符,如以下代码示例所示:
示例 13-1 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 读取即可。