Monday 30 April 2012

pImpl Idiom

If class A has an instance of class B as its member, A.h must include B.h:

A.h:


A's client must include A.h either in its header or in a source file:

clientA.h:


clientA.cpp:



By including A.h, clientA.cpp has implicitly included B.h as well thus creating dependency between clientA and B. clientA now knows about B but it should know only about A. B is a part of the A's implementation which should be hidden from clientA. If anything changes in B.h, it is not just A that has to be recompiled but testA as well. We want to break this compiler dependency.

The most common way to achieve this is through breaking up class A into two parts: publicly accessible shell class, with public interface and minimal dependency on other headers and private implementation class, which depends on other headers. For this Private IMPLementation moment, this idiom got its name - PIMPL. (Or because shell class contains Pointer to IMPLementation class - see the next paragraph.)

Class A declaration contains forward declaration of implementation class (AImpl) and pointer to it, both of which are private. RAII semantics is achieved by using boost::scoped_ptr (std::unique_ptr could have been used as well).

B.h:


B.cpp:


A.h:


A.cpp:


main.cpp:


Output:
A::A()
AImpl::AImpl()
A::foo()
AImpl::foo(). n = 3
AImpl::foo(). res = 1
A::~A()
AImpl::~AImpl()

If we decide to change B's interface, e.g. divide() to return type double instead of int, we just need to recompile unit A but not clientA:

B.h:


B.cpp:


A.cpp:


Output:
A::A()
AImpl::AImpl()
A::foo()
AImpl::foo(). n = 3
AImpl::foo(). res = 1.5
A::~A()
AImpl::~AImpl()

References:
Pointer To Implementation (pImpl)
GotW #100: Compilation Firewalls
Pimpl Idiom
Private class data pattern (Wikipedia)
Opaque pointer (Wikipedia)
pimpl idiom vs. bridge design pattern (SO)
pimpl: shared_ptr or unique_ptr (SO)
std::auto_ptr or boost::shared_ptr for pImpl idiom? (SO)
Private members in pimpl class? (SO)


No comments: