向未打开的std::ofstream写入数据

5

最近我写了一些代码,我认为还不错,但是我的同事说它会在我们的应用程序中导致随机崩溃。 有问题的代码正在写入未打开的ofstream。 我的问题是:是否可以安全地向尚未打开的ofstream写入内容? 当一个类的初始化不能为记录调试信息而打开其ofstream时就会出现这种情况。 但是后续的方法仍然会使用未打开的ofstream。 下面是一个例子:

class A {
  public:
    A(const std::string& fname) {
        if (!fname.empty()) {
            m_debug_log.open(fname.c_str());
        }
    }

    void DoSomething() {
        m_debug_log << "doing something useful now" << std::endl;
    }

  private:
    std::ofstream m_debug_log;
};

我的同事说他通过在所有对m_debug_log的输出操作上进行ofstream有效性检查来停止了随机崩溃。这样,只有在m_debug_log是有效的输出流时才会执行输出操作。

    void DoSomething() {
        if (m_debug_log)
            m_debug_log << "doing something useful now" << std::endl;
    }

当然,只有在流有效时才写入是最合理的。但我从未想到向未打开的ofstream写入会导致正确性问题。(是的,这样效率很低,但当时编码速度是我的最高优先事项。)
我快速搜索了一下,但没有找到关于这个问题的明确解释。特别是我没有看到任何关于在写入未初始化的ofstream时发生未定义行为的明确说明。最初的实现应该是正确的吗?我的问题是普遍的问题,而不是特定实现的问题。就我所知,我经常使用VS 2010、VS 2013、Ubuntu 12.04和Centos 6.3,在初始测试中没有发现任何问题。只有在运行时间较长时才会出现崩溃。

6
这里的教训是,如果一个操作可能失败,你应该始终检查是否存在失败情况。 - Some programmer dude
2
如果一个操作没有意义,比如向已关闭的流写入数据,那么你就不应该在第一时间执行它。 - user207421
1
我过去曾经使用类似的技术来记录日志,而且仅仅写入到一个关闭的流本身并不会导致问题,因为在任何操作之前都会验证流状态。如果对此浪费一些计算时间太多,或者如果调试输出执行了一些昂贵的计算,则检查流状态是一个好建议。我建议你在其他地方寻找错误。 - Ulrich Eckhardt
请查看此答案:https://dev59.com/iofca4cB1Zd3GeqPoNag - Tasos Vogiatzoglou
我认为一个正确初始化的对象,在使用合法参数调用其函数时不应该崩溃。所以,完全构造好(但未打开)的 std::fstream 应该仍然是稳定的。不过,在标准中找到明确提到这一点会很有趣。 - Galik
这听起来更像是由插入的检查掩盖了未定义行为,这是一件可怕的事情。我会还原代码并搜索实际原因,不幸的是它可能出现在任何地方。 - molbdnilo
1个回答

2

一个默认构造的(未打开的)std::ofstream在写入任何内容后会变为bad。随后的写入应该会默默地(并且安全地!)失败。

除非微软的实现有些奇怪,否则我怀疑随机崩溃是来自其他地方。确保m_debug_log完全构造完成,即使只使用默认构造函数。您确定真正的DoSomething不是静态方法吗?


你关于隐式和安全地失败的说法应该是正确的,但这并不是我们所经历的。表现问题的平台不是微软,而是一种类似于Android的特殊用途操作系统,问题也不是立即发生的。需要一些时间才能崩溃。但是,如果删除所有写入调试日志的内容,问题就会消失。同时,请注意,问题也不在被编写代码中。当我们使用非空文件名进行初始化时,崩溃也会消失。 - Phil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接