在类的 ~dtor 中调用保存函数(ala boost-serialize)会产生什么负面/未定义的行为?
4 回答
您有两个担忧,其中一个是另一个的结果:
1)你不应该允许任何异常来逃避析构函数。如果你这样做了,并且如果析构函数作为堆栈展开的一部分被调用,那么运行时将是terminate()
你的程序。这不是未定义的行为,但它是非常消极的。
因此(当然也因为析构函数不返回值):
2)你的析构函数没有合理的方式来指示成功或失败(“合理”的意思,没有建立某种单独的错误报告系统)。由于您的类的用户可能想知道保存是否发生,最好使用合理的 API 来执行此操作,这意味着析构函数只能在“尽力而为”的基础上保存数据。如果保存失败,那么对象仍然会被破坏,因此它的数据可能会丢失。
有一种针对这种情况的策略,例如文件流使用的策略。它是这样工作的:
- 有一个
flush()
(或在你的情况下save()
)保存数据的功能 - 如果对象尚未保存/刷新,则从析构函数调用此函数(或者更有可能:无条件调用它,但让函数本身知道它是否需要做任何实际工作)。在文件流的情况下,这通过
close()
. 捕获它可以抛出的任何异常并忽略任何错误。
这样一来,需要知道保存是否成功的用户就可以打电话save()
查询。不关心的用户(或者在抛出异常并且对象作为堆栈展开的一部分被销毁的情况下,如果可能的话,他们不介意它成功)可以让析构函数尝试。
也就是说,你的析构函数可以尝试做一些可能失败的事情,作为最后的努力,但你应该另外为用户提供一种“正确”做同样事情的方法,以告知他们成功或失败的方式。
是的,这确实意味着使用流而不刷新它们并检查流状态是否失败并没有“正确”使用它们,因为您无法知道数据是否曾经被写入。但是在某些情况下这已经足够好了,在同样的情况下,你的类保存在它的析构函数中可能就足够了。
问题是可能boost-serialize
会引发异常。这意味着如果析构函数被调用是因为异常正在传播并在展开时清理堆栈,那么如果对象的析构函数抛出另一个异常,您的应用程序将终止。
总而言之,您总是希望一次只传播一个异常。如果您最终得到的结果不止一个,那么您的应用程序将关闭,这违背了异常的目的。
这是个坏主意。
- 析构函数永远不应该抛出,IO 操作很可能会抛出,因为 IO 是否成功基本上是您无法控制的。
- 至少对我来说,这是非常不直观
的。一方面,它确保该类型的每个对象都将被序列化(除非析构函数有检查以防止这种情况)
b。析构函数有一个非常明确的目的,清理,存储数据基本上是清理的反面。
所以我只想再强调一点,通过在析构函数中序列化你有什么好处。
如果您正在使用 RAII,您知道即使出现异常,序列化也会运行。但这并没有太大的好处,因为即使析构函数会运行,您也不能保证序列化会运行,因为它会抛出(至少在这种情况下)。你也会失去很多正确处理故障的能力。
不,这不是一个坏主意,但也不是一个非常好的主意!但有时这样做是正确的。
只要你保护你的析构函数不抛出异常,就没有什么可以反对的。