Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does RAII work when a constructor throws an exception?

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?

like image 539
newprogrammer Avatar asked Feb 03 '12 00:02

newprogrammer


2 Answers

You're misunderstanding the first quote. That's not hard, since it's confusing.

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.

That's what it says. Here's what it meant:

if a member object with RAII semantics has been created and an exception happens in the outer object before the outer object's constructor has completed then the member object's destructor will be called as part of the stack unwinding.

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:

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

When you create a SomeType instance, it will call InnerType::InnerType. As long as that doesn't throw, it will then enter SomeType's constructor. When that throws, it will cause val to be destroyed, thus calling InnerType::~InnerType.

like image 151
Nicol Bolas Avatar answered Sep 17 '22 12:09

Nicol Bolas


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):

  1. All local variables in the constructor have their destructors invoked, releasing all resources they've acquired (if any).
  2. All of the direct subobjects of the object whose constructor threw an exception will have their destructors invoked, releasing resources they've acquired (if any).
  3. All base classes of the object whose constructor threw will have their destructors invoked (since they were fully constructed before the derived class constructor ran)
  4. Further cleanup from the caller etc. will take place.

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!

like image 30
templatetypedef Avatar answered Sep 19 '22 12:09

templatetypedef