我应该在析构函数中写下文件的结尾吗?

时间:2013-12-20 09:29:52

标签: c++ file-io destructor exception-safety

我有一些看起来有点像这样的代码:

void writeToStream( std::ostream & outputStream )
{
    MyXmlWriter xmlWriter{ outputStream };
    xmlWriter.addNode();
    xmlWriter.addNode();
    xmlWriter.close(); // should this be called in `MyXmlWriter` destructor?
}

close函数会写一些xml关闭标记,以便正确解析文件。构造函数编写xml文件的标头。可以考虑xmlWriter.close();清理代码。 C ++的常见建议是将清理代码放入析构函数中。这样你就永远不会忘记清理干净。但是,在我们的例子中,清理代码可能会抛出。 (想象一下,file可以启用异常,对文件的写入可能会失败。)因此,如果在析构函数中调用close()函数,那么它应该包含在try-catch块中抛出所有异常:

MyXmlWriter::~MyXmlWriter() 
{
    try
    {
        close();
    }
    catch (...)
    {
    }
}

但是,在这种情况下,不会通知来电者任何错误。函数writeToStream()可能无法将关闭的xml标记写入文件,而调用者不知道它。在这种情况下,最佳做法是什么?

2 个答案:

答案 0 :(得分:5)

吞咽异常通常是“最糟糕的做法”,因为它违背了首先投掷的目的。

但是在这种情况下,你真的只想要在析构函数中使用一部分功能,不包括刷新,这是一个“奖励”但是有可能抛出。尝试冲洗可能仍然存在副作用,例如不必要地等待已经发生的网络超时。

正如James Kanze所提到的,最佳做法是在析构函数运行之前手动刷新,这排除了析构函数中的异常情况。

将来C ++可能会更好地支持事务。但就目前而言,您的方法是合理的。在任何情况下,它都是指定std::filebuf的析构函数的工作方式:

  

效果:销毁类basic_filebuf<charT,traits>的对象。致电close()。如果在销毁对象期间发生异常,包括对close()的调用,则会捕获异常但不会重新抛出异常(参见17.6.5.12)。

答案 1 :(得分:4)

你在结束什么?通常,必须在销毁之前关闭打开以进行写入的文件(std::ostreamFILE*或系统相关文件描述符),以便在完成关闭后检查错误并报告它们。但是,有一些例外,特别是包装一个打开文件的类通常应该在它们的析构函数中关闭它(不检查错误,因为你无法对它们做任何事情),以确保在它的情况下进行适当的清理。例外。

据推测,关闭前的异常意味着已经存在错误,并且不会使用正在写入的文件。我通常将输出包装在具有commit函数的类中。 commit关闭,并检查错误。如果在commit之前调用析构函数,它会关闭,而不检查错误,然后删除正在写入的文件,因为它可能不完整或不可用。