【问题标题】:C++ is it a bad idea to serialize in the destructor, and if so why?C++ 在析构函数中序列化是一个坏主意,如果是,为什么?
【发布时间】:2013-11-07 23:54:06
【问题描述】:

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

【问题讨论】:

  • 您是在尝试序列化正在销毁的对象还是其他东西
  • @aaronman 哦,不,只是出于好奇。对我来说,这似乎是保存的理想时间,就在对象即将死亡的时候。
  • 好的,因为如果它不是被销毁的实际对象,您可能可以设置一个标志或提醒另一个函数序列化的东西(您不想直接调用该函数)

标签: c++ serialization destructor boost-serialization


【解决方案1】:

您有两个担忧,其中一个是另一个的结果:

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

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

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

对于这种情况有一种策略,例如文件流使用的策略。它的工作原理是这样的:

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

这样,需要知道是否保存成功的用户可以拨打save()查询。不关心的用户(或者不介意在抛出异常并且对象作为堆栈展开的一部分被销毁的情况下成功)可以让析构函数尝试。

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

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

【讨论】:

  • 那么,你认为这是一个坏主意吗??
  • @Edwin:提问者没有提供足够的信息让我发表意见,所以我回答了问题的“为什么”部分,但没有回答简单的“告诉我做什么”部分。
  • 很好的答案,但是你应该突出最后一句话。
【解决方案2】:

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

总而言之,您总是希望一次只传播一个异常。如果您最终得到多个,那么您的应用程序将关闭,这违背了异常的目的。

【讨论】:

  • @Mehrdad 我的坏 +1 来弥补它
【解决方案3】:

这是个坏主意。

  1. A destructor should never throw,IO操作很可能会抛出,因为IO成功与否基本上是你无法控制的。
  2. 至少对我来说这是非常不直观的
    一种。一方面,它确保该类型的每个对象都将被序列化(除非析构函数有检查以防止这种情况发生)
    湾。析构函数有一个非常明确的目的,清理,存储数据基本上是清理的反面。

所以我只想再强调一点,通过在析构函数中序列化你有什么好处。


如果您使用 RAII,即使出现异常,您也知道序列化会运行。但这并没有太大的好处,因为即使析构函数会运行,您也不能保证序列化会运行,因为它会抛出(至少在这种情况下)。你也会失去很多正确处理故障的能力。

【讨论】:

  • 如果你没有直截了当地说:这是个坏主意,我会投赞成票的。
  • @Edwin 我认为这是一个坏主意,即使你处理了抛出问题,它最终也会非常不直观地从析构函数存储在文件中
  • 那你的直觉是错误的(直觉大多是,当然除非你是女人,哈哈)。
  • 我认为直觉没有错,但它并不能涵盖所有情况。正如我在回答中提到的那样:标准流将数据保存在其析构函数中,因此您的 C++ 直觉应该允许这样做。直觉是正确的,您不应该将其作为析构函数的目的来保存数据。只是,如果对象在销毁时还有未完成的事情,那么这是它最后一次完成它的机会。
  • @SteveJessop OP 表示这是一种假设情况,因此很难就解决此问题的正确方法形成意见,而无需继续进行下去,我敢打赌,如果他在那里举了一个例子在析构函数中序列化是一种更好的处理方式
【解决方案4】:

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

只要你保护你的析构函数不抛出异常,没有什么可以反对的。

【讨论】:

  • 实际上这是一个非常糟糕的主意,请在此处阅读其他答案
  • @aaronman:到目前为止,你是唯一一个说它不好的人。到目前为止,另一个答案并没有说明它的好坏。我认为你错了。
  • 适合自己,IMO Caesars 的回答绝对不建议这样做
  • @aaronman:推荐和说这是一个坏主意是完全不同的两件事。而且我也不推荐它,但有时这是唯一/正确的做法。
猜你喜欢
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-11
  • 2012-03-06
  • 2011-06-22
  • 1970-01-01
相关资源
最近更新 更多