The pimpl-pattern

Today a colleague and I discussed whether the pimpl-pattern was such a good idea to be used. He argued that it hides implementation details which might remind you of the internal implementation of the respective class while looking at its header file. Thus, it’s easy for you to miss stuff that you have already implemented but forgot about, eventually leading to you implementing it twice (or even more often).

Of course, he has a certain point about this. I countered that this is only the case if that person is really looking at the header file, instead of the implementation file, leading to the fundamental discussion mentioned above. In the end, I was able to convince him that the pimpl-pattern is useful by three arguments. But before enlisting them, let’s look again at how the pimpl-pattern can be implemented in C++.

You might declare a class like this:

// header file for Widget class
#include "gadget.h";
 
class Widget
{
 public:
  // public interface of Widget //
 private:
  float _topLeftCoordinates[3];
  float _bottomRightCoordinates[3];
 
  Gadget _gadget;
};

A typical class definition. Now let’s see how the class would be declared employing the pimpl-pattern and the boost library:

// header file for Widget class
#include <boost/shared_ptr.hpp>
 
class Widget
{
 public:
  // public interface of Widget //
 private:
  boost::shared_ptr<struct WidgetPrivate> _pimpl;
};

The interesting part of the implementation for this example would look like this:

// implementation file for widget class
#include "widget.h"
#include "gadget.h"
 
struct WidgetPrivate
{
  float _topLeftCoordinates[3];
  float _bottomRightCoordinates[3];
 
  Gadget _gadget;
};
 
Widget::Widget( /* constructor parameters */ )
: _pimpl(new WidgetPrivate)
// rest of the constructor's implementation //

This little example demonstrates all advantages about the pimpl-pattern:

  • It makes the public interface to a class easier to identify: When a class is implemented in terms of the pimpl-pattern, all a reader needs to filter out is the line where the private object is declared. Everything else is part of the class’ interface and thus important to the reader. On the other hand, the actual implementation of the class is hidden from the user, making him rely on the interface, not the (assumed) implementation of the class, which might change over time.
  • It reduces compile time dependencies: By moving non-trivial object instanciations (here: _gadget) – which need to be allocated and therefore defined on object construction – to the implementation file, we need only very few includes in the header file. This results in less compile-time dependencies, reducing compilation times.
  • Last, but not least: A class implemented with the pimpl-pattern can be easier adapted to use the lazy-copying algorithm. As the complete state of each object is encoded in a separate structure (here: WidgetPrivate), it suffices to have a copied object refer to the original’s private implementation as long as there is no write-access to it. This can save a lot of time!

One of the disadvantages of this pattern has been mentioned above and I’d like to mention the other as well: Every access to a class’s private members now incurs another indirection. Depending on how often the functions of your class are used, this might result in a noticeable run-time cost. However, according to the famous 80-20 rule, this disadvantage won’t affect many classes…

In case you wondered: Of course the pimpl-pattern can be implemented without boost just as well. It’s just that with boost’s shared_ptr implementation, we don’t need to worry about deleting our private struct as opposed to using standard pointers and won’t get compiler warnings as opposed to using std::auto_ptr (which doesn’t like forward declared structs/classes).

Leave a Reply

You must be logged in to post a comment.