October 1997

Structuring your code to avoid leaks

by Dan P. Plunkett

Exceptions are an exciting addition to C++ because they allow you to write clearer and safer code. Many C++ experts will also tell you that by using exceptions, you’ll end up with code that’s both easier to maintain and that contains fewer bugs. I firmly agree, although with a few caveats.

Improperly used, exceptions can lead to resource leaks that are very hard to find. In this article, we’ll present a technique called resource allocation is initialization. This technique encapsulates the allocation and eventual deallocation of resources, freeing you from doing it yourself.

Here’s the problem

Consider the following code snippet:
bool process_file(const char *fname)
{
   FILE *f;

   if ((f = fopen(fname, “wb”)) == 0)
      return false;
   read_file(f);
   fclose(f);
   return true;
}
At first glance, this function doesn’t appear to have a problem. However, there’s a potential for leaking a file handle. What if the read_file function throws an exception? Since there’s no try/catch block protecting the call to fclose, any exception thrown by read_file won’t close the file handle. You might try to remedy this dilemma as follows:
bool process_file(const char *fname)
{
   FILE *f;
   if ((f = fopen(fname, “wb”)) == 0)
      return false;
   try
      {
      read_file(f);
      }
   catch (…)
      {
      fclose(f);
      throw; /* rethrow exception */
      }
   fclose(f);
   return true;
}
While this approach solves the resource leak, it’s tiresome, prone to error, and not very elegant. Thankfully, there’s an easier way to resolve the problem. Remember the property that automatic variables have? Their destructors are called whenever they fall out of scope, including each time an exception is thrown. With this in mind, I’ll introduce the following class:
class File
{
   FILE *p;
public:
   File(const char *fname, char *mode)
      {
      if ((p = fopen(fname, mode)) == 0)
         throw “Error opening file”;
      }
   ~File() { fclose(p); }
   operator FILE*() { return p; }
};
With this class, the function becomes trivial (and safer):
void process_file(const char *fname)
{
   File f(fname, “wb”);
   read_file(f);
}
By encapsulating the allocation and deallocation of the file handle into a class and taking advantage of the inherent properties of destructors, we’ve removed from you the responsibility of freeing the file handle. Now, regardless of whether an exception is thrown, the file handle will always be freed. Note that the File class contains a casting operator that allows the class to function exactly like a FILE pointer. Therefore, the following code is still perfectly legal:
void f(void)
{
   File f(“test.tmp”,”rb”);
   char buffer[80];

   fread(buffer, sizeof(buffer), 1, f);
   …
}

Applying the technique to memory allocation

Memory is the most frequently leaked resource in any application, and such leaks are often difficult to find. Fortunately, you can evolve the resource allocation is initialization scheme to handle memory allocations as well:
template  class Memory
{
   T *p;
public:
   Memory(int size) { p = new T[size]; }
   ~Memory() { delete [] p; }
   operator T*() { return p; }
};
This template allows you to encapsulate all your memory allocations in such a way as to guarantee that they’re freed when they fall out of scope, as follows:
void f(void)
{
   Memory buffer(512);

   //…use buffer
}
As you can see, you can easily extend this solution to any type of resource: file handles, memory, locks, semaphores, and database cursors, just to name a few.

auto_ptr is your friend

Buried in memory.h, which is included with Borland C++Builder, is a generic template class called auto_ptr, which implements the resource allocation is initialization scheme generically so you can use it with any VCL object. For example, does your application ever need to instantiate TQuery in a function? Use auto_ptr to ensure that the database cursor is properly destroyed before your function exits, as follows:
void __fastcall TForm::ButtonClick(
  TObject *Sender)
{
   auto_ptr Query(new TQuery(this));
   //…use Query
}

Conclusion

Finding and correcting resource leaks can be one of the most expensive tasks in a development effort. By using the powerful resource allocation is initialization technique properly, you’ll reduce the cost of developing and maintaining your application. Considering that software development comprises more than 90 percent of the expense of an overall system, minimizing costs to correct such leaks is of paramount importance.