应该将输出操作放入析构函数中吗?

Should one put output operations into a destructor?

本文关键字:析构函数 输出 操作      更新时间:2023-10-16

我有一个类,它在程序执行期间收集和处理一些数据,我们称它为dataCollectionInterface。当程序终止时(或者更确切地说:一个dataCollectionInterface对象超出了作用域),需要对收集到的(部分)数据进行一些最终处理和输出。

现在的问题是:我应该把这个最终的处理和输出(到文件)到dataCollectionInterface的析构函数中(或者从析构函数中调用它)?或者我应该提供一个需要由主程序显式调用的公共成员例程(doFinalProcessing)吗?

将其放入析构函数中会方便得多(无需担心在调用doFinalProcessing等之后防止数据调制的保护措施),但是是否有缺点,例如,关于输出操作可能出现的异常的处理?

你不应该从析构函数抛出任何异常,所以如果你的操作可能抛出异常,你需要为它们做异常处理,最好在公共函数中而不是析构函数中做。

但是,如果您可以在析构函数本身中处理所有异常,而不是将它们从析构函数中抛出,那么您也可以采用第一种机制,如果您可以可靠地这样做,我认为没有什么害处。

析构函数不能失败。因此,您不应该将任何操作放在在析构函数中可能失败(输出也可能失败);如果发生了,你会怎么做失败。

也有例外,当操作与程序的总体功能(例如,记录输出),或者当它是一个保护(ofstream将关闭文件在析构函数,忽略任何错误),或者当它是操作的一部分时哪个稍后会被"取消":未提交的事务可能会关闭一个输出文件,例如,知道,因为事务不是提交后,文件将在稍后被删除。但它们只是:例外。

其他人关于析构函数不抛出的说法是正确的,但这并不意味着不能在析构函数中抛出。这也不意味着没有诊断方法。但是你一定要把它放在析构函数中。

首先,将其放在析构函数中的原因不是为了立即方便,而是因为僵尸是邪恶的。那些已经结束了使用寿命但仍然存在的对象是开发人员的祸根。年轻的开发者在他们不应该引起各种问题的时候介入并接触他们。复杂性爆炸了,因为你现在必须检查你是否正确地清理了东西。异常处理现在不是自动的,就像在析构函数中那样。你真的想在你使用对象的任何地方都写try/catch,仅仅因为一些不相关的东西可能会抛出,你必须正确地清理它吗?你想在你所有的代码中为这个类写"if"来确保他们没有使用一个几乎死的对象吗?

两阶段初始化或销毁是你没有正确使用tors/dr的代码气味。使用它们来自动化所有对象的生命周期(RAII),您将永远不会有僵尸带来的代码脆弱性。

那么,如果输出操作可能抛出,该如何处理呢?

  • 将可能的抛出调用包装在try/catch中。
  • 在对象的构造函数中,从用户获取一个可选的回调函数,用于错误处理例程。
  • 在医生,当一个异常被捕获,调用回调(如果他们给你一个)与任何诊断,你可以提供
  • 在try/catch中包装回调调用,因为你不知道其他人会做什么
  • 如果回调抛出什么也不做——他们有他们的机会

就是这么简单。永远不要让僵尸漫游,即使你在医生中有特殊的例外可能代码。相反,提供一种以抽象方式处理它的方法。当僵尸被释放时,一个单独的特殊清理情况总是比一系列特殊情况的组合爆炸要好。

我将同时提供:具有一些默认行为的析构函数(捕获所有异常,可能对可能的问题保持沉默)和具有扩展诊断的公共成员例程。使用哪种方法取决于类的用户。在内部,析构函数可以调用带有try/catch块的例程,如果例程可能抛出。例程应该是幂等的(第二次调用不应该做任何事情)