Book Image

Learning Boost C++ Libraries

By : Arindam Mukherjee
Book Image

Learning Boost C++ Libraries

By: Arindam Mukherjee

Overview of this book

Table of Contents (19 chapters)
Learning Boost C++ Libraries
About the Author
About the Reviewers


C++ programs frequently deal with system resources like memory, file and socket handles, shared memory segments, mutexes, and so on. There are well-defined primitives, some from the C Standard Library and many more from the native systems programming interfaces, which are used to request and relinquish these resources. Failing to guarantee the release of acquired resources can cause grave problems to an application's performance and correctness.

The destructor of a C++ object on the stack is automatically invoked during stack unwinding. The unwinding happens when a scope is exited due to control reaching the end of the scope, or by executing return, goto, break, or continue. A scope is also exited as a result of an exception being thrown. In either case, the destructor is guaranteed to be called. This guarantee is limited to C++ objects on the stack. It does not apply to C++ objects on the heap because they are not associated with a lexical scope. Furthermore, it does not apply to the aforementioned resources like memory and file descriptors, which are objects of Plain Old Data types (POD-types) and therefore do not have a destructor.

Consider the following C++ code using the new[] and delete[] operators:

char *buffer = new char[BUFSIZ];
… …
delete [] buffer;

The programmer was careful to release the buffer allocated. However, if another programmer came and flippantly wrote code to exit the scope somewhere between the calls to new and delete, then buffer would never be released and you would leak memory. Exceptions could arise in the intervening code too with the same result. This is true not just of memory but of any resource which requires a manual step to release, like delete[] in this case.

This is where we can utilize the guaranteed invocation of a destructor when exiting a scope to guarantee the clean-up of resources. We can create a wrapper class whose constructor acquires ownership of the resource and whose destructor releases the resource. A few lines of code can explain this technique that usually goes by the name Resource Acquisition is Initialization or RAII.

Listing A.1: RAII in action

 1 class String
 2 {
 3 public:
 4   String(const char *str = 0)
 5   {  buffer_ = dupstr(str, len_);  }
 7   ~String() { delete [] buffer_; }
 9 private:
10   char *buffer_;
11   size_t len_;
12 };
14 // dupstr returns a copy of s, allocated dynamically.
15 //   Sets len to the length of s.
16 char *dupstr(const char *str, size_t& len) {
17   char *ret = nullptr;
19   if (!str) {
20     len = 0;
21     return ret;
22   }
23   len = strlen(str);
24   ret = new char[len + 1];
25   strncpy(ret, str, len + 1);
27   return ret;
28 }

The String class encapsulates a C-style string. We pass it a C-style string during construction, and it creates a copy of the passed string on the free store if it is not null. The helper function dupstr allocates memory for the String object on the free store using the the new[] operator (line 24). If allocation fails, operator new[] throws std::bad_alloc, and the String object never comes into being. In other words, resource acquisition must succeed for initialization to succeed. This is the other key aspect of RAII.

We use the String class in code as shown here:

   String favBand("Led Zeppelin");
 ...   ...
 } // end of scope. favBand.~String() called.

We create an instance of String called favBand, which internally allocates a character buffer dynamically. When favBand goes out of scope normally or due to an exception, its destructor is called and it releases this buffer. You can apply this technique to all forms of resources that require manual release, and it will never let a resource leak creep in. The String class is said to own the buffer resource, that is, it has unique ownership semantics.