C ++在析构函数中序列化是一个坏主意,如果是这样,为什么呢?

时间:2013-11-07 23:54:07

标签: c++ serialization destructor boost-serialization

在类的~dtor中调用保存函数(ala boost-serialize)会产生什么负面/未定义的行为?

4 个答案:

答案 0 :(得分:7)

你有两个顾虑,其中一个是另一个的结果:

1)您不应允许任何异常来逃避析构函数。如果这样做,并且如果析构函数作为堆栈展开的一部分被调用,则运行时将terminate()您的程序。这不是未定义的行为,但它非常消极。

因此(当然也因为析构函数不返回值):

2)你的析构函数没有合理的方法来指示成功或失败(“合理”的意思,没有构建某种单独的错误报告系统)。由于您的类的用户可能想知道保存是否发生,最好使用合理的API来执行此操作,这意味着析构函数只能在“尽力而为”的基础上保存数据。如果保存失败,那么对象仍然会被破坏,因此可能会丢失它的数据。

这种情况有一种策略,例如文件流。它的工作原理如下:

  • 拥有保存数据的flush()(或在您的情况下为save())功能
  • 如果对象尚未保存/刷新,则从析构函数中调用此函数(或者更可能:无条件地调用它,但让函数本身知道它是否需要执行任何实际工作)。在文件流的情况下,这通过close()发生。捕获它可以抛出的任何异常并忽略任何错误。

这样,需要知道保存是否成功的用户才能查询save()。不关心的用户(或者如果在抛出异常并且对象在堆栈展开过程中被销毁的情况下不会介意成功的用户)可以让析构函数尝试。

也就是说,你的析构函数可以尝试做一些可能失败的事情,作为最后的努力,但你应该另外提供一种方法让用户“正确地”做同样的事情,一种告诉他们成功或失败的方式。

是的,这确实意味着在没有刷新它们的情况下使用流并检查流状态是否失败并不是“正确”使用它们,因为您无法知道数据是否曾被写入。但是在某种情况下,这种情况已经足够好了,在同样的情况下,你的课程可能足以保存在析构函数中。

答案 1 :(得分:3)

问题是boost-serialize可以抛出异常。这意味着如果正在调用析构函数,因为异常正在传播并在展开时清理堆栈,那么如果对象的析构函数抛出另一个异常,则应用程序将终止。

总而言之,您始终只希望一次传播一个异常。如果你最终得到的不止一个,那么你的申请将会关闭,这会破坏例外的目的。

答案 2 :(得分:2)

这是一个坏主意。

  1. A destructor should never throw,IO操作很可能会抛出,因为IO成功或不成功基本上是你无法控制的。
  2. 至少对我来说这是非常不直观的 一个。例如,它确保该类型的每个对象都将被序列化(除非析构函数有检查以防止这种情况)
    湾析构函数有一个非常明确的目的,清理,存储数据基本上与清理相反。
  3. 所以我只想提出一点,通过序列化析构函数你需要获得什么。


    如果您正在使用RAII,即使存在异常,您也知道序列化将会运行。但这并不是一个好处,因为即使析构函数将运行,也无法保证序列化将在它抛出后运行(至少在这种情况下)。你也失去了很多正确处理失败的能力。

答案 3 :(得分:1)

不,这不是一个坏主意,但它也不是一个非常好的主意! 但有时这是正确的做法。

只要你保护你的析构函数不会抛出异常,就没有任何反对意见。