繁体   English   中英

当构造函数抛出异常时,RAII如何工作?

[英]How does RAII work when a constructor throws an exception?

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

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

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

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

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

如果构造函数抛出异常,则不会运行该对象的析构函数。 如果您的对象已经完成了需要撤消的操作(例如分配一些内存,打开文件或锁定信号量),则必须通过对象内的数据成员记住这些“需要撤消的内容”。

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

那么这些场景中究竟发生了什么?

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

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

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

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

看到不同? 这个想法是成员对象完成了它的构造函数,但拥有的类型却没有。 它在构造函数中抛出某个地方(或者在该构造函数之后初始化的另一个成员的构造函数)。 这将导致所有成员的析构函数被调用(所有已完成构造的构造,即),而不是它自己的析构函数。

这是一个例子:

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

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

这里没有矛盾; 在不同的环境中使用了一些令人困惑的术语。

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

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

因此,由智能指针或作为被破坏对象的数据成员的其他RAII对象管理的任何资源都将被清除,但是在对象的析构函数中执行清理的专用代码将不会触发。

希望这可以帮助!

这两个陈述并不矛盾,但第一个陈述有一些不幸的语言。 当某个对象的构造抛出时,它的解构函数将不会被调用,但该对象拥有的所有对象将被其各自的解构器破坏。

因此,对于RAII和智能指针,对象的任何指针成员的析构函数将独立于到期对象的析构函数进行调用。 原始指针不释放它们指向的内存,必须手动删除。 是否应该释放拥有对象的构造函数抛出原始指针。 智能指针不会发生这种情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM