Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Exception Unsafe

3.00/5 (1 vote)
26 Apr 2012CPOL2 min read 8.4K  
What it means for code to satisfy the basic exception safety guarantee.

I was reading up about exception safe code in C++ and the difference between the strong and basic exception guarantees. The article here (http://www.gotw.ca/gotw/082.htm) outlines what it means for code to satisfy the basic exception safety guarantee, however it doesn’t give an example. So I came up with something that is exception unsafe. The code below doesn’t satisfy the basic guarantee because it fails to delete the memory allocated to the pointer.

Exception Unsafe

C++
#include <iostream>
#include <stdexcept>

using namespace std;

void h()
{
  throw logic_error((string("Failure in ") + string(__func__)));
}

void g()
{
  int* pi = new int;
  h();
  delete pi;
}

void f()
{
  try
  {
    g();
  }
  catch (const exception& e)
  {
    cout << e.what() << endl;
  }
}

int main(int argc, char** argv)
{
  f();
}

To ensure that it meets the basic guarantee, the exception needs to be caught in g() and the memory deallocated. The code below does this. Unfortunately, there is a bit of awkwardness here: note the double delete. One is in the bad path where the exception is caught and the other is in the good code path. It would be nice if the double delete wasn’t necessary. Other languages have a finally statement that is executed in both the good and bad case, however C\++ doesn’t have this but the problem is still solvable as shown below.

Basic Exception Safety Met

C++
#include <iostream>
#include <stdexcept>

using namespace std;

void h()
{
  throw logic_error((string("Bad logic in ") + string(__func__)));
}

void g()
{
  int* pi = new int;
  try
  {
    h();
  }
  catch (const exception& e)
  {
    cout << "Caught exception: \"" << e.what() << "\" in " << __func__  << endl;
    delete pi;
    throw;
  }
  delete pi;
}

void f()
{
  try
  {
    g();
  }
  catch (const exception& e)
  {
    cout << "Caught exception: \"" << e.what() << "\" in " << __func__  << endl;
  }
}

int main(int argc, char** argv)
{
  f();
}

The lack of a finally statement in C++ isn’t a problem as long as the principle Resource Acquisition is Initialization is applied. It’s fairly straightforward and with a little bit of thought, I think that most good programmers would realize what to do here in order to avoid having a double delete. The trick is to write a wrapper class to manage the resource and in the destructor, clean up any allocated memory. If you follow this guideline, then there is no need for C++ to have a finally statement. This is mentioned in the C++ FAQ in section 17.6. The following code applies RAII. I’ve found myself doing this in code I’ve written without explicitly realizing the technique I was applying had a name associated with it.

Basic Exception Safety with RAII

C++
#include <iostream>
#include <stdexcept>

using namespace std;

class Resource
{
  private:
    int* pi;

  public:
    Resource()
    {
      cout << __func__ << endl;
      pi = new int;
    }

    ~Resource()
    {
      cout << __func__ << endl;
      delete pi;
    }
};

void h()
{
  throw logic_error((string("Bad logic in ") + string(__func__)));
}

void g()
{
  Resource r;
  h();
}

void f()
{
  try
  {
    g();
  }
  catch (const exception& e)
  {
    cout << "Caught exception: \"" << e.what() << "\" in " << __func__  << endl;
  }
}

int main(int argc, char** argv)
{
  f();
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)