7

作者在标题下展示了这段代码A bus error on my platform

#include <fstream>
#include <iostream>

int main()
{
    std::ofstream log("oops.log");
    std::cout.rdbuf(log.rdbuf());
    std::cout << "Oops!\n";
    return 0;
}

字符串“Oops!\n”被打印到文件“oops.log”中。代码没有恢复cout的streambuf,但是VS2010没有报运行时错误。

4

3 回答 3

10

由于logstd::cout共享一个缓冲区,该缓冲区可能会被释放两次(一次log超出范围,然后再一次在程序终止时)。

这会导致未定义的行为,因此很难说出它在他的机器上触发总线错误但在你的机器上静默失败的确切原因。

于 2013-02-13T18:21:20.457 回答
5

由于其他答案没有提到该怎么做,所以我会在这里提供。您需要保存和恢复 cout 应该管理的缓冲区。例如:

#include <fstream>
#include <iostream>

// RAII method of restoring a buffer
struct buffer_restorer {
    std::ios &m_s;
    std::streambuf *m_buf;

    buffer_restorer(std::ios &s, std::streambuf *buf) : m_s(s), m_buf(buf) {}
    ~buffer_restorer() { m_s.rdbuf(m_buf); }
};

int main()
{
    std::ofstream log("oops.log");
    buffer_restorer r(std::cout, std::cout.rdbuf(log.rdbuf()));
    std::cout << "Oops!\n";
    return 0;
}

现在,当cout' 的缓冲区cout在程序结束时被销毁之前被替换时,所以当cout销毁它的缓冲区时会发生正确的事情。


对于简单的重定向标准 io,通常环境已经有能力为你做这件事(例如,在 shell 中的 io 重定向)。而不是上面的代码,我可能只是简单地将程序运行为:

yourprogram > oops.log

另外要记住的一件事是,它std::cout是一个全局变量,与其他全局变量具有所有相同的缺点。而不是修改它甚至使用它,您可能更喜欢使用通常的技术来避免全局变量。例如,您可以传递一个std::ostream &log_output参数并使用它,而不是cout直接使用代码。

于 2013-02-13T19:07:39.410 回答
3

您的程序具有未定义的行为。

全局对象的析构函数cout会在超出范围时删除流缓冲区,log同样拥有同样的流缓冲区也是如此。因此,您将两次删除同一个对象。

当程序具有未定义的行为时,任何事情都可能发生,从格式化硬盘到终止而没有任何错误。

例如,在我的平台上,程序从main().

于 2013-02-13T18:22:28.077 回答