【问题标题】:How does RAII work when a constructor throws an exception?当构造函数抛出异常时,RAII 是如何工作的?
【发布时间】:2012-02-25 16:44:56
【问题描述】:

我正在学习 C++ 中的 RAII 习语,以及如何使用智能指针。

在我的阅读中,我遇到了两件在我看来似乎相互矛盾的事情。

引用自http://www.hackcraft.net/raii/

...如果已创建具有 RAII 语义的成员对象并且在构造函数完成之前发生异常,则其析构函数将作为堆栈展开的一部分被调用。因此,控制多个资源的对象可以保证它们的清理,即使它不是使用成员 RAII 对象完全构造的。

但引用自http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10:

如果构造函数抛出异常,则对象的析构函数不会运行。如果您的对象已经做了一些需要撤消的事情(例如分配一些内存、打开文件或锁定信号量),那么这个“需要撤消的东西”必须由对象内部的数据成员记住。

然后第二个链接源建议使用智能指针来处理已经在构造函数中分配的东西的问题。

那么在这些场景中实际发生了什么?

【问题讨论】:

  • +1 这是“newprogrammer[s]”应该提问的方式!

标签: c++ exception constructor raii


【解决方案1】:

您误解了第一句话。这并不难,因为它令人困惑。

如果已创建具有 RAII 语义的成员对象并且在构造函数完成之前发生异常,则其析构函数将作为堆栈展开的一部分被调用。

这就是它所说的。这就是它的意思

如果已创建具有 RAII 语义的成员对象,并且在 外部对象的构造函数完成之前在外部对象中发生异常,则 该成员对象的 析构函数将作为堆栈展开的一部分调用。

看到区别了吗?这个想法是成员对象完成了它的构造函数,但拥有类型没有。它在其构造函数中的某个位置(或在该构造函数之后初始化的另一个成员的构造函数)。这将导致其所有成员的析构函数被调用(即所有完成构造的成员),但不是它自己的析构函数。

这是一个例子:

class SomeType
{
  InnerType val;
public:
  SomeType() : val(...)
  {
    throw Exception;
  }
};

当您创建SomeType 实例时,它将调用InnerType::InnerType。只要不抛出,它就会进入SomeType的构造函数。当它抛出时,它将导致val 被销毁,从而调用InnerType::~InnerType

【讨论】:

  • 啊,这很有道理...顺便问一下,您是编写出色的 3D 图形指南的人吗?我非常感谢您,该指南非常棒。
  • "这将导致调用其所有成员的析构函数,但不会调用它自己的析构函数。" - 好吧,只是在抛出异常之前已经完成构建的那些......
【解决方案2】:

这里没有矛盾;只是在不同的上下文中使用了一些令人困惑的术语。

如果对象的构造函数抛出异常,则发生以下情况(假设异常被捕获):

  1. 构造函数中的所有局部变量都调用了它们的析构函数,释放它们获取的所有资源(如果有的话)。
  2. 构造函数引发异常的对象的所有直接子对象都将调用其析构函数,释放它们获取的资源(如果有)。
  3. 构造函数抛出的对象的所有基类都将调用其析构函数(因为它们是在派生类构造函数运行之前完全构造的)
  4. 调用者等会进一步清理。

因此,由智能指针或其他 RAII 对象管理的任何资源(它们是被破坏对象的数据成员)确实会被清除,但在对象的析构函数中执行清除的专用代码不会触发.

希望这会有所帮助!

【讨论】:

    【解决方案3】:

    这两种说法并不矛盾,但第一种说法有些令人遗憾。当某个对象的构造抛出时,它的解构函数不会被调用,但该对象拥有的所有对象都将被它们各自的解构函数销毁。

    因此,对于 RAII 和智能指针,对象的任何指针成员的析构函数都将独立于所属对象的析构函数被调用。原始指针不会释放它们指向的内存,必须手动删除。如果拥有对象的构造函数抛出原始指针,则不会被释放。智能指针不会发生这种情况。

    【讨论】:

      猜你喜欢
      • 2013-10-01
      • 1970-01-01
      • 2019-08-12
      • 2017-11-07
      • 2020-06-13
      • 2011-11-04
      • 1970-01-01
      • 2012-04-30
      • 1970-01-01
      相关资源
      最近更新 更多