Skip to content

Writing a Real C++ Program – Part 8

August 22, 2011

This is the eighth instalment in a series of C++ programming tutorials that started here.

Introduction

In the previous instalment, you dynamically created an instance of a class derived from the Reporter abstract base class. The code in essence looked like this:

Reporter * p = new CSVReporter( cout );
// do stuff with p
delete p;

This is all well and good, but it does have a couple of problems. Firstly, suppose you forgot to make that call to delete? It seems unlikely that you would do that, but in a larger, more complex programming it’s a surprisingly easy thing to do. And if you do forget, the C++ compiler will not remind you or call delete for you – you will have a “memory leak”. In a program like scheck, this is not such a big deal, as when the program exits all the memory you allocated will be reclaimed by the operating system. But if the code was in a loop, in a long running process (say a server or a GUI tool), the leaked memory can quickly mount up, to the point where all  real memory is used up. At that point, the operating system will start paging and/or swapping, and things will start to run very slowly indeed, or probably just crash.

The other case where a memory leak can occur is when the code represented by the “do stuff with p” comment throws an exception. If that happens, the destructor will not be called unless you write code that looks like this:

Reporter * p = new CSVReporter( cout );
try {
    // do stuff with p
}
catch( ... ) {
    delete p;
}

Unfortunately, just about any code in a C++ program may throw exceptions, so you end up having to write try/catch blocks for all your code. This is time-consuming and actually rather difficult (sometimes almost impossible) to do properly. In fact, you should avoid writing try/catch blocks at all, wherever possible.

C++ actually has a neat solution to this problem. If you don’t create objects dynamically, then their destructors are guaranteed to be called by C++ under (almost) all circumstances. If you could write the code like this:

{
    Reporter p = CSVReporter( cout );
    // do stuff with p
    // destructor called here
}

the problem would be solved. Unfortunately, you do need to create the reporter instance dynamically. The C++ solution  (as is often the case) is to introduce a new class – a smart pointer.

Smart Pointers 101

A smart pointer is a class which encapsulates a raw C++ pointer and provides a destructor  which deletes the thing pointed to (the pointee) when the smart pointer goes out of scope. A very simple smart pointer class which could manage Reporter-derived types might look something like this:

class SmartReporterPtr {
  public:
    SmartReporterPtr( Reporter * p ) : mPtr( p ) {
    }
    ~SmartReporterPtr() {
      delete mPtr;
    }
    Reporter * operator ->() {
      return mPtr;
    }
  private:
    Reporter * mPtr;
};

You could then use the smart pointer like this:

{
  SmartReporterPtr p( new CSVReporter( cout ) );
  p->ReportHeader();
  // other use of the Reporter interface
  // SmartReporterPtr destructor calls delete on Reporter
}

In fact, this smart pointer would work for any type you cared to specify, and so is a prime candidate for being made  a template class:

template <typename T>
class SmartPtr {
  public:
    SmartPtr( T * p ) : mPtr( p ) {
    }
    ~SmartPtr() {
      delete mPtr;
    }
    T * operator ->() {
      return mPtr;
    }
  private:
    T * mPtr;
};

which could be used like this:

{
  SmartPtr <Reporter> p( new CSVReporter( cout ) );
  p->ReportHeader();
  // other use of the Reporter interface
  // SmartPtr destructor calls delete on Reporter
}

Although it looks like implementing smart pointers yourself is pretty easy, you should avoid doing so. There are lots of edge cases, conversion issues and other things that are very easy to get wrong, and it is almost always a better idea to use one of the implementations provided by the C++ Standard Library.

One particular point to notice about the above code is that it won’t deal properly with arrays, as delete will be called rather than delete [], leading to undefined behaviour. Although there are smart pointers available that can handle arrays, you should not normally be using such things – use std::vector instead.

Standard C++ Smart Pointers

The C++ Standard Library supplies quite a selection of smart pointers, of which I’m going to comment on three. First off the blocks is std::auto_ptr, which is declared in the header file <memory>. This was the first smart pointer to make it into the C++ Standard, and the first one to make it out, as it is deprecated in the 2011 Standard. Deprecation means that it is still available, but may be removed from the Standard in later versions. In fact, the chances of it being removed, and if it were removed of it being unimplemented by compiler suppliers is approximately zero.

The auto_ptr class has the advantage of being very portable (it is supported by every C++ implementation) but has lots of rather unpleasant features. I don’t wish to write an exhaustive critique here, but suffice it to say that its copying and assignment semantics make it unsuitable for use inside C++ containers such: as vectors and maps. Here’s how you would use an auto_ptr in the scheck code:

Parser p( sub );
auto_ptr <Reporter> rep;
if ( argc == 1 ) {
    rep = auto_ptr <Reporter> ( new CSVReporter( cout ) );
}
else {
    rep = auto_ptr <Reporter> ( new XMLReporter( cout ) );
}
string word;
rep->ReportHeader();    
while( ( word = p.NextWord() ) != "" ) {
    if ( ! d.Check( word ) ) {
        rep->ReportError( word, p.Context(), p.LineNo(), subtext );
    }
}
rep->ReportFooter();

Note that the constructor that takes a pointer for auto_ptr (and for all of the C++ smart pointers) is declared as explicit. This prevents accidental conversion errors, but means that whenever you need to construct an auto_ptr from an ordinary pointer, you need an explicit constructor call, which is a bit of a pain.

If auto_ptr is deprecated, how about shared_ptr? This smart pointer has become something of the poster-child for smart pointers, mostly because it can be used just about anywhere (you can use it in collections, for example) and will normally work. Also, you can freely copy shared pointers. This results in it being commonly recommended as a solution on sites like StackOverflow.  However, this flexibility comes with some overhead. The shared_ptr class is reference counted, which means that it is internally considerably more complicated than auto_ptr, and also means that in multi-threaded situations it can possibly be a performance bottle-neck. The shared_ptr class is available as part of the new 2011 standard, or as part of the Boost library.  If you are using the GCC compiler, you can make it accept 2011 (also known as C++0x) features like this:

g++ -std=c++0x main.cpp

The -std option says which C++ Standard you want the compiler to use.

Lastly, there is auto_ptr‘s replacement – unique_ptr. This is part of the 2011 Standard and in fact cannot be implemented on compilers that do not support the new move semantics available in that standard. This smart pointer works like auto_ptr, but can be used in collections.

So, which to use? In the scheck code above, it’s a toss-up between auto_ptr and unique_ptr. I have chosen to use auto_ptr in order to make the code work for those who’s compilers do not yet support the 2011 Standard. But at the end of the day, what is more important than exactly which smart pointer you use is that you do use one! A very simple rule – never, ever allocate something with new and assign the resulting value to a plain pointer. Always use a smart pointer of some type.

RAII

Smart pointers are a special case of a common C++ programming idiom called RAII. This stands for “resource acquisition is initialisation” and was coined by Bjarne Stroustrup, who may not exactly be the world’s #1 at giving things catchy names. What RAII means is that resources, which are things like memory, file handles, sockets, window handles etc. should always be controlled by a class with a destructor, and not allocated directly using dynamic allocation or operating system functions. You’ve already used a lot of classes that behave this way – all the C++ Standard Library containers, the string class and the file stream classes use RAII to manage memory, file handles etc. And you should do the same – if one of your classes allocates resources, make the destructor de-allocate them.

Use Of Inheritance

The Reporter and its derived classes are the first major use of inheritance in the scheck code. This may surprise you if you have been taught to believe that programming in C++ is all about creating inheritance hierarchies. In fact it isn’t – the vast majority of good C++ code does not use inheritance much. One place where it definitely should not be used is in the design of applications. Notice that your use of the Reporter classes emerged from the existing code, as a way of simplifying the selection of behaviour – this is a typical motivator for the use of inheritance.

Conclusion

This has been a rather theoretical instalment, and I did not get around to talking about testing, which will have to wait until next time. For an exercise, I suggest you change the code in main to use smart pointers – you might like to experiment to see which ones your particular compiler supports.

Sources for this and all other tutorials in the series are available here.

Advertisements

From → c++, linux, tutorial, windows

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: