Contents
The pImpl idiom is a useful idiom in C++ to reduce compile-time dependencies. Here is a quick overview of what to keep in mind when we implement and use it.
What is it?
The pImpl Idiom moves the private implementation details of a class into a separate structure. That includes private data as well as non-virtual private methods.
The key to this idiom is to only forward-declare the implementation struct in the class header and own onw instance via a pointer. With naming conventions of prefixing pointers with p
the pointer is often named pImpl
, giving the idiom its name. The naming convention may differ, e.g. in Qt it’s d
– sticking to a name is useful to make the idiom recognizable.
//MyClass.h
#include <memory>
class MyClass {
public:
explicit MyClass(int i);
//...
int getSomething() const;
void doSomething();
private:
struct Impl;
std::unique_ptr<Impl> pImpl;
};
//MyClass.cpp
#include <MyClass.h>
struct MyClass::Impl {
int i;
void twice() {
i *= 2;
}
void half() {
i /= 2;
}
};
MyClass::MyClass(int i)
: pImpl{new Impl{i}}
{}
int MyClass::getSomething() const {
return pImpl->i;
}
void MyClass::doSomething() {
if (pImpl->i % 2 == 0) {
pImpl->half();
} else {
pImpl->twice();
}
}
//...
What is it used for?
The use of the pImpl idiom is twofold: it can greatly reduce compile time dependencies and stabilize the ABI of our class.
Compile time firewall
Because of the reduced dependencies, the pImpl idiom sometimes is also called a “compile time firewall”: Since we move all data members into the opaque Impl
struct, we need to include the headers that declare their classes only into the source file. The classes of function parameters and return types need only be forward-declared.
This means that we need only include <memory>
for the unique_ptr
, headers of base classes, and the occasional header of typedefs for which forward declarations are not possible. In the end, translation units that include MyClass.h
have potentially fewer headers to parse and compile.
ABI stability
Changes to private implementation details of a class usually mean that we have to recompile everything. Changes in data members mean that the layout and size of objects change, changes in methods mean that overload resolution has to be reevaluated.
With pImpl, that is not the case. The class will always only have one opaque pointer as the only member. Private changes do not affect the header of our class, so no clients have to be recompiled.
How to impl the pImpl
The example above shows a sketch of how we can implement the pImpl idiom. There are some variations and caveats, and the //...
indicates that I’ve left some things out.
Rule of 5
The Impl struct is only forward-declared. That means the compiler can not generate the destructor and other member functions of the unique_ptr
for us.
So, we have to declare them in the header and provide an implementation in the source file. For the destructor and move operations, defaulting them should suffice. The copy operations should either be explicitly deleted (they are implicitly deleted due to the unique_ptr
) or implemented by performing a deep copy of the impl structure.
MyClass::MyClass(MyClass&&) = default;
MyClass::MyClass(MyClass const& other)
: pImpl{std::make_unique<Impl>(*other.pImpl)}
{}
MyClass::~MyClass() = default;
MyClass& MyClass::operator=(MyClass&&) = default;
MyClass& MyClass::operator=(MyClass const& other) {
*pImpl = *other.pImpl;
return *this;
}
The Impl struct
The Impl struct should be simple. Its only responsibility is to be a collection of the private details of the outer class. That means, it should not contain fancy logic in itself, only the private methods of the outer class.
It also means that it does not need its own header since it is used in one place only. Having the struct in another header would enable other classes to include it, needlessly breaking encapsulation.
Inner class or not?
The impl struct can be either an inner class of the actual class, or it can be a properly named standalone class, e.g. MyClassImpl
or MyClassPrivate
. I usually choose the private inner structure so that the access to its name is really restricted to the implemented class, and there are no additional names in the surrounding namespace. In the end, the choice is mostly a matter of preference – the important thing is to stick to one convention throughout the project.
What not to do
Don’t derive from the Impl struct
I’ve heard of deriving from the Impl struct as an argument to put it in its own header. The use case of deriving would be overriding parts of the implementation in a derived class of the outer class.
This will usually be a design smell since it mixes the aggregation of private details with polymorphism by making those details not so private at all. If parts of the base class behavior have to be overridden, consider using the strategy pattern or similar behavioral patterns and provide a protected method to exchange the strategy.
Don’t overuse it
The pImpl idiom comes at a cost: Allocating memory is relatively costly in terms of performance. It’s possible to use specialized allocators, but that only trades the performance cost for complexity, and it’s not scalable to a large number of classes. That’s why using the pImpl idiom everywhere just because we can is a bad idea.
Permalink
Much more details, incl. about const propagation, to be found here: https://marcmutz.wordpress.com/translated-articles/pimp-my-pimpl/
Article is old, but not, yet, outdated.
</shameless plug>
Permalink
The point is to have the Impl declaration outside of the class header, so how can the Impl class be an inner class ?
Permalink
This basically mimics virtual methods (like intended by using a pure base class as an interface) but in a more expensive way.
Also, since the constructor of the base class still needs to know the private implementation the separation isn´t total either.
Additionally, how does the smart pointer (or any other means of destruction of the implementation) know how to properly destruct the implementation structure? Without an explicit destructor taking care of it or a implementation base class enforcing a virtual destructor (both additionally weakening the separation) this is sooner a later a likely cause of resource leakage.
Note that a class with only non-virtual methods and an opaque data pointer would provide full compile-time independency with zero runtime and far less code overhead as all dependency resolving gets have move to the link or even dynamic loading (in case of a DLL) stage. Proper destruction is simpler there as well.
Thus, pImpl is far from a great tool for just reducing build-time dependencies.
However, as an additional separation mechanism for more complex demands (like Qt) or provide inter-compiler compatibility it can be pretty useful.
About the const issue: Indeed, references are your friend here. A small “smart reference” helper template as a non-zeroable with transitive const usage is a pretty useful tool.
Permalink
I don’t agree with the suggested implementation due to the use of the std::unique_ptr.
The reason is that the unique_ptr does not respect the const-ness of the function or object. You can test this by changing the getSomething function as follow:
int MyClass::getSomething() const {
return ++pImpl->i;
}
Thus the function states (promises) that it does not change the object, but it actually does change the underlying data in the object.
Then “const” on the function does not mean anything.
Personally if I see a unique_ptr being used as a pimpl pointer I know I can ignore the contract/promises of “const” on any function or interface on that object.
Permalink
Const is not transitive through any pointer, so you have thus problem with any way to implement pImpl. And, of course, with any other class that uses pointers of any kind to aggregate its parts.
Permalink
it should be better when std::experimental::propagate_const comes around.
Permalink
int MyClass::getSomething() const {
return pImpl->i;
}
danger: it breaks the const (ie pImpl->i=10; will compile here)
it should to be
class foo {
struct impl;
public:
~foo() noexcept ;
// interface here..
private:
std::unique_ptr pimpl_;
impl& pimpl() { return pimpl_; }
const impl& pimpl() const {return pimpl_; }
};
and inside foo’s use only pimpl(), pimpl_ can be used only in ctor. in all const methods it is
pimpl_inside * const
insteed
const pmpl_inside*
Permalink
In the Microsoft Windows DLL world, there is another reason to use pImpl – to force all class allocations and deallocations to occur in the DLL. Because of the vagaries of the various C runtime libraries, it is dangerous to provide a DLL that returns allocated memory that the caller is expected to free. Unfortunately, one can also not use unique_ptr<>, even with a custom DLL-provided deleter. There is no guarantee that the unique_ptr<> the DLL was compiled with has the same memory layout as the unique_ptr<> the client was compiled with. So, you are safer using a raw pointer</holds nose>.
Permalink
I once created a generic Pimpl implementation which was essentially a wrapper around the unique_ptr you have but it specifies the gang of 5 depending on whether you want them or not. This way, the class having a pimpl could still use the rule of 0 which is very convenient.
And by means of a template parameter you could control whether the pimpl struct was copyable and/or moveable. I’m paraphrasing a bit, but the essence is here: https://godbolt.org/z/gA0p-0
The caveat of this approach is that the constructor of the class becomes inlined. So if the class is part of a dll interface, this may be a bad idea
Permalink
Qt actually gives a reason for deriving from the private/Impl class: build a hierarchy of private classes, matching the hierarchy of public classes, and installing the pImpl pointer just in the topmost class of the hierarchy.
For instance, you have QObject, which holds the pImpl pointer (to a QObjectPrivate object¹). QWidget inherits from QObject, and of course needs its own data, stored in a QWidgetPrivate object. Where do you put this data?
You can add another pImpl member to QWidget, pointing to QWidgetPrivate. But this comes with drawbacks: it adds an extra memory allocation (creating a QWidget means allocating QObjectPrivate, for the base QObject class, plus QWidgetPrivate; and if we repeat this pattern, the more we derive, the more allocations we perform), and causes a name clash for the data member in QWidget (either we come up with a clever naming scheme, or we have to use awkward syntax to access the pImpl pointer of the base classes).
Qt instead makes QWidgetPrivate inherit from QObjectPrivate². The pImpl pointer in the base QObject class is reused, and there’s only one allocation for the private class. A few macros (Q_DECLARE_PRIVATE, Q_D, etc.³) are used to downcast the pImpl pointer to the right type needed by a derived class. The glue for all of this is a protected constructor for QObject that takes a QObjectPrivate pointer (and installs it as the pImpl pointer)⁴: QWidget constructors use protected this constructor for the base class, passing a QWidgetPrivate⁵. In turn, QWidget has a similar constructor to be used by its subclasses⁶.
¹ https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qobject.h.html#QObject::d_ptr
² https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidget_p.h.html#QWidgetPrivate
³ https://code.woboq.org/qt5/qtbase/src/corelib/global/qglobal.h.html#M/Q_DECLARE_PRIVATE
⁴ https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qobject.h.html#_ZN7QObjectC1ER14QObjectPrivatePS
⁵ https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidget.cpp.html#_ZN7QWidgetC1EPS_6QFlagsIN2Qt10WindowTypeEE
⁶ https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidget.h.html#_ZN7QWidgetC1ER14QWidgetPrivatePS_6QFlagsIN2Qt10WindowTypeEE