Private-Implementation is a Software Engineering paradigm which allows us to enhance encapsulation, reduce compilation times and retain binary compatibility. The idea here to add another layer of abstraction between the library and the user code.
Consider that you are create a library which you will be shipping to customers when done. Since Software is never perfect and always extensible, after you shipped you Version 1.0 you plan to change the way your memory allocator functions . So you start updating the library and ship another version. You don’t change the signature of the function, just the internals. So at the backbox view, everything is same, just the feeling this version would be better than the previous since its Version 2.0. But, there is always a ‘but’, you have to add some private member in the .h file. So along with the Version 2.0 library you also ship update .h file.
Now let’s say that I am one of the users of your great library. I use it to develop UI framework for my mobile device. So now when you release Version 2.0, there are two options for me. Either I stick to older less efficient version of upgrade to 2.0. I decide to upgrade to 2.0, I place the new .h and library to correct location and start compiling my code.
Now to my surprise I see that my complete code base (which is 10000 files big and more that 10,000,000 lines of code, lets assume it) is rebuilding. Now why did that happen. None of your changes affect the way my code works. I mean you didn’t change signature of any of your functions (beware, that’s a very bad practice in ‘Library Developer World’) or add new functionality, you only changed how your ‘new’ and ‘delete’ operators work internally which I don’t care until they do what is advertised. But I see lot of my code being recopiled.
PIMPL helps you avoid the issue by adding an extra layer of abstraction between the application code and library.
Lets say that the name of your library class is Foo. In which you have implemented getMessage(), setMessage(std::string) and printMessage() functions. What PIMPL idiom suggests that you create another class FooImpl in which you have all the members variables and functions of Foo, declared and implemented as if it was Foo, as shown in listing below.
void setMessage(char* );
#endif // FOOIMPL_H
void FooImpl::setMessage(char* message)
Now create another class Foo which will act as abstraction class for FooImpl. Look as code below.
void setMessage(char* );
FooImpl* m_fooImpl; //d_ptr
#endif // FOO_H
Foo::Foo(): m_fooImpl(new FooImpl())
void Foo::setMessage(char message)
#include “Foo.h” //no need to include “FooImpl.h”
int main(int argc, char *argv)
char* myMessage=”Hello PIMPL”;
Let me quickly explain what is happening in the code.
You access everything in FooImpl class, using private member variable, m_fooImpl of Foo.cpp class. The client or user of the library does know nothing about FooImpl. All he has to do is create an object of Foo class and access its public members as if they were implemented in Foo class.
Now the thing to note here is that any change you make in FooImpl class will not need you to recompile main.cpp or Foo. This helps us to avoid recompiling our code incase of changes in library. And since Foo class doesn’t include any implemention other than pointer indirection to FooImpl.
You can check by copying above code in respective .cpp and .h files and compiling with your favorite compiler. Try to see what files are compile upon changing each file.
But this idiom does have following drawbacks.
* More work for the implementor.
* Doesn’t work for ‘protected’ members where access by subclasses is required.
* Somewhat harder to read code, since some information is no longer in the header file.
* Run-time performance is slightly compromised due to the pointer indirection, especially if function calls are virtual (branch prediction for indirect branches is generally poor).
Author : Devendra Patel