On Writing Exception Safe Code - Part 1

Are the following constructor and destructor exceptions safe and exception neutral?


template <class T> 
class Stack {
public:
  Stack();
  ~Stack();

private:
  T *v_;         // ptr to a memory area big
  size_t vsize_; // enough for 'vsize_' T's
  size_t vused_; // # of T's actually in use
};

template<class T> 
Stack<T>::Stack()
 : v_(new T[10]), // default allocation
   vsize_(10),
   vused_(0) // nothing used yet
{}

template <class T> 
Stack<T>::~Stack() {
  delete[] v_; // this can't throw
}

Let's define exception safety and exception neutrality.

Exception Safety

Stack objects should always be in a correct and consistent state, regardless of any exceptions that might be thrown in the course of executing Stack's member functions.

Exception Neutrality

If any exceptions are thrown, they should be propagated seamlessly through to the caller, who can deal with them as he pleases, because he knows the context of T and we don't.

The standard requires that when an exception occurs and does not get handled, it gets propagated up to the call stack until it reaches a point that can handle it or until the program terminates.

Why can’t new call throw?

With the new keyword, if an exception occurs in the constructor, C++ automatically deallocates the memory allocated with the new and releases it back to the system. Even if the object is partially constructed. So whether the new operator succeeds or fails, the Stack object will be in a consistent state.

But what about the vsize_ which is set to 10? It is also consistent because even if vsize_ is set, no memory was allocated, so the value of vsize_ does not matter.

Basically, if an object’s constructor exits due to an exception, the object is considered to have never existed (its lifetime never started) and any memory that was briefly or partially allocated is automatically destroyed by C++.

Why can’t delete[] call throw?

The standard also requires the delete[] operator to have a specific signature that does not allow it to throw exceptions.


void operator delete[]( void* ) throw();
void operator delete[]( void*, size_t ) throw();

These signatures indicate that the delete[] operator must not throw exceptions during memory deallocation.

A safer and modern C++ way of preventing a function from throwing is the use of noexcept keyword.