当构造函数抛出异常时,RAII 如何工作?
我正在学习 C++ 中的 RAII 惯用法,以及如何使用智能指针。
在我的阅读中,我发现了两件事,对我来说,这似乎是相互矛盾的。
引用自 http://www.hackcraft.net/raii/:
...如果已创建具有 RAII 语义的成员对象,并且在构造函数完成之前发生异常,则将调用其析构函数作为堆栈展开的一部分。因此,控制多个资源的对象可以保证其清理,即使它不是使用成员 RAII 对象完全构造的。
但引用自 http://www.parashift.com/c++- faq-lite/exceptions.html#faq-17.10:
如果构造函数抛出异常,则对象的析构函数不会运行。如果您的对象已经完成了需要撤消的操作(例如分配一些内存、打开文件或锁定信号量),则必须由对象内的数据成员记住此“需要撤消的操作”。
然后第二个链接源建议使用智能指针来处理构造函数中已分配的内容的问题。
那么这些场景中实际发生了什么?
I am learning about the RAII idiom in C++, and how to use smart pointers.
In my reading, I have come across two things that, to me, seem to contradict each other.
Quoted from http://www.hackcraft.net/raii/:
...if a member object with RAII semantics has been created and an exception happens before the constructor has completed then its destructor will be called as part of the stack unwinding. Hence an object which controls multiple resources can guarnatee their cleanup even if it isn’t fully constructed by using member RAII objects.
But quoted from http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10:
If a constructor throws an exception, the object's destructor is not run. If your object has already done something that needs to be undone (such as allocating some memory, opening a file, or locking a semaphore), this "stuff that needs to be undone" must be remembered by a data member inside the object.
And then the second linked source recommends using smart pointers to deal with the issue of things that were already allocated in the constructor.
So what actually happens in these scenarios?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你误解了第一句话。这并不难,因为它令人困惑。
就是这么说的。这就是它的含义:
看到区别了吗?这个想法是成员对象完成了它的构造函数,但所属类型却没有。它在其构造函数(或在该成员之后初始化的另一个成员的构造函数)中抛出了某处。这将导致调用其所有成员的析构函数(即所有完成构造的成员),但不调用其自己的析构函数。
下面是一个示例:
当您创建
SomeType
实例时,它将调用InnerType::InnerType
。只要不抛出异常,它就会进入SomeType
的构造函数。当抛出异常时,它将导致val
被销毁,从而调用InnerType::~InnerType
。You're misunderstanding the first quote. That's not hard, since it's confusing.
That's what it says. Here's what it meant:
See the difference? The idea is that the member object completed its constructor, but the owning type didn't. It threw somewhere in its constructor (or a constructor of another member that is initialized after that one). This will cause the destructor of all of its members to be called (all of the ones that completed construction, that is), but not its own destructor.
Here's an example:
When you create a
SomeType
instance, it will callInnerType::InnerType
. As long as that doesn't throw, it will then enterSomeType
's constructor. When that throws, it will causeval
to be destroyed, thus callingInnerType::~InnerType
.这里并不矛盾;只是在不同的上下文中使用了一些令人困惑的术语。
如果对象的构造函数抛出异常,则会发生以下情况(假设捕获了异常):
因此,由智能指针或作为被破坏对象的数据成员的其他 RAII 对象管理的任何资源确实会被清理,但不会触发在对象的析构函数中进行清理的专门代码。
希望这有帮助!
There's no contradiction here; there's just some confusing terminology being used in different contexts.
If an object's constructor throws an exception, then the following occurs (assuming the exception is caught):
As a result, any resources that are managed by smart pointers or other RAII objects that are data members of the object being destructed will indeed be cleaned up, but specialized code to do cleanup in the destructor of the object won't fire.
Hope this helps!
这两种说法并不互相矛盾,但第一种说法有些不幸。当某个对象的构造抛出异常时,它的解构函数不会被调用,但该对象拥有的所有对象都将被其各自的解构函数破坏。
因此,使用 RAII 和智能指针,对象的任何指针成员的析构函数将独立于所属对象的析构函数进行调用。原始指针不会释放它们指向的内存,必须手动删除。如果拥有对象的构造函数抛出原始指针,则不会被释放。智能指针不会发生这种情况。
These two statements don't contradict each other, but the first one has some unfortunate language. When the construction of some object throws, it's deconstructor won't be called, but all objects owned by that object will be destructed by their individual deconstructors.
So with RAII and smart pointers the destructors for any pointer members of an object will be called independently of the destructor of the owing object. Raw pointers do not free the memory they point to and have to be deleted manually. Should the constructor of the owning object throw raw pointers will not be freed. This cannot happen with smart pointers.