【问题标题】:Semantics of flags on basic_iosbasic_ios 上标志的语义
【发布时间】:2011-05-14 14:52:12
【问题描述】:

我发现自己反复被 rdstate() 标志 - good()bad()eof()fail() - 以及它们在 basic_ios::operator!operator booloperator void* 中的表达方式所困扰。

谁能让我摆脱痛苦并解释一下,这样我就不必再三思了?

【问题讨论】:

    标签: c++ input c++-faq


    【解决方案1】:

    有三个标志指示错误状态:

    • badbit 表示流出现了严重问题。它可能是缓冲区错误或向流提供数据的任何错误。如果设置了此标志,您可能不会再使用该流。

    • failbit 表示从流中提取或读取失败(或输出流的写入或插入),您需要注意该失败。

    • eofbit 表示输入流已结束,没有任何内容可读取。请注意,这仅在您尝试从已到达其末尾的输入流中读取之后设置(即,当您尝试读取不存在的数据而发生错误时设置它)。

    failbit 也可能由许多到达 EOF 的操作设置。例如,如果流中只剩下空白并且您尝试读取int,您将同时到达EOF,并且您将无法读取int,因此将设置两个标志。

    fail() 函数测试badbit || failbit

    good() 函数测试!(badbit || failbit || eofbit)。也就是说,当没有设置任何位时,流是好的。

    您可以使用ios::clear() 成员函数重置标志;这允许您设置任何错误标志;默认情况下(不带参数),它会清除所有三个标志。

    流不会超载operator bool()operator void*() 用于实现安全布尔成语的一个有点损坏的版本。如果设置了 badbitfailbit,则此运算符重载返回 null,否则返回非 null。您可以使用它来支持测试提取成功作为循环或其他控制流语句的条件的习惯用法:

    if (std::cin >> x) {
        // extraction succeeded
    }
    else {
        // extraction failed
    }
    

    operator!() 重载与operator void*() 相反;如果设置了badbitfailbit,则返回true,否则返回falseoperator!() 重载不再需要了;它可以追溯到完全一致地支持运算符重载之前(请参阅 sbi 的问题 "Why does std::basic_ios overload the unary logical negation operator?")。

    C++0x 修复了导致我们必须使用安全布尔习语的问题,因此在 C++0x 中,basic_ios 基类模板确实将 operator bool() 重载为显式转换运算符;此运算符与当前的operator void*() 具有相同的语义。

    【讨论】:

    • Good 被错误命名(因此被误用),因为测试 good() 不会告诉您最后一次操作是否成功。换句话说,stream.good() 不等同于 bool(stream)。
    【解决方案2】:

    除了James' answer,重要的是要记住这些标志表示操作的结果,因此除非您执行,否则不会设置。

    一个常见的错误是这样做:

    #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;
    }
    

    【讨论】:

    • +1 因为强调这一点很重要。在 C++ 中,I/O 错误地太频繁了。
    • 非常感谢,这个问题昨天也出现在 q 上,(惊喜!)一开始让我感到困惑,尽管仔细想想还是有道理的。
    猜你喜欢
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-16
    • 2014-11-25
    • 2011-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多