7

可能重复:
如果 C++ 中的构造函数抛出异常,如何清理初始化的资源

如果我正在创建 6 个对象并且这些对象创建 5 个对象并且在创建第 6 个对象时失败,我该如何处理构造函数中的异常?

谢谢。

4

3 回答 3

3

通常的行为只是让异常传播。将调用任何完全构造的基类和成员的析构函数;如果前五个对象是成员,它们将被正确销毁。

唯一可能出现问题的情况是您所讨论的对象是动态分配的(使用new)。如果是这样:首先要问自己的是为什么?为什么要动态分配,而不是使对象成为具体成员?以我的经验,这种需求非常非常少,除了少数特殊情况(例如编译防火墙习语),在这种情况下,类中通常只有一个对象(例如,指向实现对象的指针) . 在这种情况下,没有问题,因为如果该new对象的失败,则没有其他需要撤消的操作。

如果您发现自己处于非常罕见的情况下,您确实必须使用动态分配并且拥有多个这样的对象(例如,因为您有两个多态的子对象),那么您必须确保每个分配都是包裹在某种子对象中(智能指针可以解决问题);一旦第一个子对象被成功构造,如果构造函数在稍后的某个时间点失败,它的析构函数将被调用。

于 2012-10-04T09:43:31.607 回答
1

当构造函数中抛出异常时,所有完全构造的子对象都将被销毁。由于使用构造对象的析构函数来管理它们的资源是一种很好的做法,因此无需对这些子对象进行任何操作。剩下的就是在抛出异常时清理当前正在执行的构造函数的主体。但是,这与任何其他功能中的清理没有什么不同。

请注意,销毁顺序与构造相反。也就是说,当所有子对象还没有被销毁时,身体中的清理首先开始。然后成员被销毁,然后是非虚拟基类,最后是虚拟基类。

于 2012-10-04T09:40:30.820 回答
1

处理异常的“核心”是几乎所有东西都应该通过析构函数来清理。例如,如果你“新建”一个对象,你会得到一个“原始”指针;如果在某处引发异常,您必须确保该原始指针已正确“删除”——但请确保您没有删除尚未初始化的原始指针。

另一方面,如果将该指针存储到 std::unique_ptr 中,则无需执行任何操作;当 unique_ptr 被销毁时,对象被删除,并且对象销毁自动发生:当 unique_ptr 超出范围时,编译器调用清理,完全不可见(因此不再因大量清理调用而导致代码混乱)并自动(所以不再有“哎哟,当它走上一条没有人真正测试过的罕见路径时,它会忘记清理”)。

这同样适用于几乎所有资源。COM 对象有“自动指针”(例如,在 DirectX 中使用),大多数框架应该给你一个“范围锁”类型的对象来包裹互斥锁(所以它在创建对象时锁定互斥锁,并且当它被破坏时将其解锁),并且您可以编写微小的包装器来处理各种 Windows 句柄。

基本上,如果您将所有清理工作都放入析构函数中,您将永远不必为了清理工作而“尝试...捕获...重新抛出”。“更大”对象的析构函数通常非常简单,因为实际上所有“包含”对象都由它们的析构函数自动清理。

于 2012-10-04T10:57:59.037 回答