Author image

Pointer to Implementation (Pimpl) - Bridge Design Pattern


Difficulty:
1/5


In the non-C++ world Pimpl is also known as the Bridge design pattern, with the following definition:

Decouple an abstraction from its implementation so that the two can be extended independently.

in the vain of Bridge, in C++, when anything in a header file class definition changes, all users of that class must be recompiled - even if the only change was to the private class members that the users/clients of the class cannot even access. This is because C++'s build model is based on textual inclusion, and because C++ assumes that callers know two main things about a class that can be affected by private members: 1. Size and Layout 2. Functions

Because private data members of a class participate in its object representation, affecting size and layout, and because private member functions of a class participate in overload resolution (which takes place before member access checking), any change to those implementation details requires recompilation of all users of the class.

pImpl breaks this compilation dependency by storing only a pointer to another class; changes to the implementation do not cause recompilation. Consequently, if a library uses pImpl in its ABI, newer versions of the library may change the implementation while remaining ABI-compatible with older versions.

The client doesn't need to change a thing - the change is transparent to him and he won't even take notice. And that is intentional (typically) as we don't want to upset the clients. We don't want them asking too many questions and meddle with our business either! They're always unaffected by the nitty gritty details that occurs under the hood.

There are two basic files of a class in C++:

1) interface.h

2) impl_client.cpp

Typically the clients possess the interface and the programmers both the interface and of course the implementation. With PIMPL the programmers can freely make changes to the implementation files without requiring our clients to recompile (since headers are not compilable files - they are only included to the implementation .cpp files).

Design

  • interface functions implemented in the .cpp file except the public .getPimpl() member which can be defined in the header
  • implementation class “Impl” is declared and defined in the .cpp file. Just forward declare it inside the header file class Impl*/std::unique_ptr<class Impl>
  • use regular pointer for many clients, or unique_ptr for moving the pimpl such that only one client can have exclusive control of it
  • If you are using copy semantics the interface class' copy constructor and copy assignment operator arguments must not be const and you'd declare the pointer to implementation using a regular raw pointer: *pImpl. It is recommended you use move semantics (as we do here) so you can delete them and use unique_ptr<Impl> pImpl;.
  • Use a getImplementation() method to gain access to the private implementation object Impl. You can freely  have anything you like within the implementation.

You can also recognize a pimpl/bridge pattern when there are not pointers involved at all, but you simply declare and define the class in an implementation .cpp file. Code in the header file can only make forward declaration references to it, but the user will never see it.

I used Windows 8.1 x86_64, Visual Studio 2017 to build the project.

Github

Github repository link.


0 likes