我发现自己反复被这些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*()
用于实现安全布尔惯用语的一个有点损坏的版本。badbit
如果设置了or ,则此运算符重载返回 null failbit
,否则返回非 null。您可以使用它来支持测试提取成功作为循环或其他控制流语句的条件的习惯用法:
if (std::cin >> x) {
// extraction succeeded
}
else {
// extraction failed
}
operator!()
过载是相反的operator void*()
;true
如果设置了badbit
or则返回failbit
,false
否则返回。operator!()
不再需要重载;它可以追溯到完全一致地支持运算符重载之前(参见 sbi 的问题“为什么 std::basic_ios 重载一元逻辑否定运算符?”)。
C++0x 修复了导致我们必须使用安全 bool 习惯用法的问题,因此在 C++0x 中,basic_ios
基类模板确实operator bool()
作为显式转换运算符重载;this 运算符与 current 具有相同的语义operator void*()
。
除了James 的回答之外,重要的是要记住这些标志表示操作的结果,因此除非您执行一个,否则不会设置。
一个常见的错误是这样做:
#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;
}