我发现自己一次又一次地被rdstate()
标志所困惑 - good()
, bad()
, eof()
, fail()
- 以及它们在basic_ios::operator!
, operator bool
和 operator void*
中的表达方式。
有人能够让我解脱,并解释一下这个问题,这样我就再也不用思考了吗?
有三个标志表示错误状态:
badbit
表示流发生了严重错误。可能是缓冲区错误或者是提供数据给流的内容出现了错误。如果设置了此标志,那么你很可能不再使用该流。
failbit
表示从流中提取或读取失败(对于输出流来说,写入或插入失败),你需要意识到这种失败。
eofbit
表示输入流已经到达其末尾,没有剩余可读取的内容。请注意,只有在尝试从已经到达末尾的输入流中读取时才会设置此标志(也就是说,当尝试读取不存在的数据时会出现错误)。
failbit
也可能由许多到达 EOF 的操作设置。例如,如果流中仅剩空格,并尝试读取一个 int
,那么你将同时到达 EOF 并且无法读取 int
,因此两个标志都将被设置。
fail()
函数测试 badbit || failbit
。
good()
函数测试 !(badbit || failbit || eofbit)
。也就是说,当没有任何标志被设置时,流是良好的。
你可以使用 ios::clear()
成员函数重置标志;这允许你设置任何错误标志;默认情况下(没有参数),它会清除所有三个标志。
流不会过载operator bool()
; operator void*()
用于实现安全bool习惯用语的一个有点瑕疵的版本。如果设置了badbit
或failbit
,则此运算符重载返回null,否则返回非null值。您可以使用此方法支持测试提取成功作为循环或其他控制流语句的条件的习惯用法:
if (std::cin >> x) {
// extraction succeeded
}
else {
// extraction failed
}
operator!()
重载是operator void*()
的相反操作;如果设置了badbit
或failbit
,则返回true
,否则返回false
。现在不再需要operator!()
重载;它可以追溯到运算符重载完全和一致支持之前(请参见sbi的问题“为什么std::basic_ios重载一元逻辑非运算符?”)。basic_ios
基类模板确实重载了operator bool()
作为显式转换运算符;该运算符具有与当前的operator void*()
相同的语义。#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream file("main.cpp");
while (!file.eof()) // while the file isn't at eof...
{
std::string line;
std::getline(file, line); // ...read a line...
std::cout << "> " << line << std::endl; // and print it
}
}
eof()
才会被设置。此时流将会说“不行,没有更多内容了!”并将其设置。这意味着“正确”的方式是:#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream file("main.cpp");
for (;;)
{
std::string line;
std::getline(file, line); // read a line...
if (file.eof()) // ...and check if it we were at eof
break;
std::cout << "> " << line << std::endl;
}
}
这将检查放置在正确的位置。虽然这很混乱,但对我们来说很幸运的是,std::getline
的返回值是流,并且该流具有转换运算符,允许它在布尔上下文中进行测试,其值为 fail()
,其中包括 eof()
。因此,我们只需要写:
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream file("main.cpp");
std::string line;
while (std::getline(file, line)) // get line, test if it was eof
std::cout << "> " << line << std::endl;
}