About Me

My photo
I'm a colonist who has declared war on machines and intend to conquer them some day. You'll often find me deep in the trenches fighting off bugs and ugly defects in code. When I'm not tappity-tapping at my WMD (also, known as keyboard), you'll find me chatting with friends, reading comics or playing a PC game.

Tuesday, February 12, 2008

The Pimpl pattern

(Reader Level : Beginner)
(Knowledge assumptions : Classes, Pointers...)

The Private Implementation (Pimpl) pattern allows the implementation of a whole interface without the need to recompile the modules which use it. As with most design patterns, this pattern can best be explained with an example. Let's say that we have a class named Student. Our Student class is very popular in our project because many other classes use it. The header file of the Student class, "Student.h" may be as follows:
#ifndef STUDENT_HEADER
#define STUDENT_HEADER

#include <string>

class Student
{
private:
std::string m_name;
};

#endif

We may have many classes that contain instances of Student. For example, a Subject class may have an aggregation of Students. So, each of the classes that contain Student(s) would probably have to include "Student.h". Now let's say that at some point in time, colleges allow students to carry mobile phones freely (I know that's not likely. :D). Now, our Student will have a contact number that we might want to record. Professors already had mobiles all along. So, let's say that we already have a class named ContactNo that stores all types of numbers, be they Landline, Mobile or Pagers. "Student.h" becomes...
#ifndef STUDENT_HEADER
#define STUDENT_HEADER

#include <string>
#include "ContactNo.h"

class Student
{
private:
std::string m_name;
ContactNo m_contactNo;
};

#endif

After making the relevant changes, we need to do a full re-compile of the source. To our dismay, we might find that the compile time has become unbearably long. Why? It's because we included "Contact.h" in our popular "Student.h". With that one simple change, the header files of all the classes that used to just include "Student.h" will now also include "Contact.h". If our Student was that popular, then this might mean a lot of unnecessary inclusions. To add insult to injury, the contact number of the Student might never even be used by many classes. For example, our Subject class would have no reason to know the contact number of the Students who take that subject. That's why m_contactNo is a private member of Student. You might think that the entire situation is a bit contrived but I assure you its not and there can be situations where you might desperately be seeking a way to reduce compile times, especially in the case of large projects. This is where the Pimpl pattern comes in. Basically, what we need to somehow achieve is the removal of "ContactNo.h" from "Student.h". What we do is take the private implementation of the class (in our case, m_contactNo of Student) and pack it into a simple internal structure. A pointer to this structure, called an opaque pointer or d-pointer is then made a private member of the class. We also need to take care of dynamically creating and destroying the implementation structure for every object of the wrapping class.

This is then our "Student.h" file with the Pimpl pattern.
#ifndef STUDENT_HEADER
#define STUDENT_HEADER

#include <string>

class Student
{
private:
std::string m_name;
struct PIMPL; //Forward declaration.
PIMPL* pimpl; //The opaque pointer.

public:
Student();
~Student();
};

#endif

Our "Student.cpp" file could be:
#include "Student.h"
#include "ContactNo.h"

//Private Implementation structure.
struct Student::PIMPL
{
ContactNo m_contactNo;
};

Student::Student()
{
pimpl = new PIMPL;
}

Student::~Student()
{
if(pimpl)
delete pimpl;
}

If we want to access the contact number of the student internally we do it as pimpl->m_contactNo. Now that we've seen how useful the Pimpl pattern can be, let's talk about its downside. By using the Pimpl pattern, our code has become a little more complex (less readable anyway) and we've also got to do a dynamic allocation for every instance of Student that is created. For large objects with relatively fewer instances, this probably won't be an issue but for small objects with several instances, Pimpl may not fit the bill.

3 comments:

Midhun Harikumar said...

hey what dows the naming convention for the variables as preceded by an m stand for

eg: m_name;

ive seen it in some places and cannot simply understand what it means.

Please reply

Angelo said...

Well, this is the naming convention that I follow:
1. A member variable of a class is prefixed with an "m_"
2. A boolean variable is prefixed with a 'b'
3. A pointer is prefixed with a 'p'

So, if I had a member function of a class that is a pointer to a string called name, I may call it "m_pName". Some programmers may deviate a bit from these convention rules or perhaps have more rules that they follow. For eg; some programmers have a prefix of 'i' for integer variables.

Unknown said...

It stands for "member"