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

Ral*_*zky 2 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?
}
Run Code Online (Sandbox Code Playgroud)

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

MyXmlWriter::~MyXmlWriter() 
{
    try
    {
        close();
    }
    catch (...)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Pot*_*ter 5

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

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

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

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

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