

The pImpl Idiom
source link: https://www.tuicool.com/articles/hit/VBRJBfi
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 ofprefixing 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 beforward-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 besimple. 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.
Recommend
-
29
相信大家在阅读C++代码的时候,一定都见过pImpl的实现方式。我第一次见到pImpl实现方式时,对于下面这种代码: ++ void MyClass::SomeFunc() { pImpl_->SomeFunc(...
-
20
This post is part of Advent of Parens 2019 , my attempt to publish one blog post a day during the 24 days of the advent. Here’s a little Cl...
-
11
Execute Around idiom in Java The Execute Around idiom is a pattern that allows you to wrap an action with some standard setup / tear down s...
-
7
Parallel assignment: a Python idiom cleverly optimized Friday, May 15, 2020 Every programmer has had to swap variables. It's common in real programs and it's a frequently used example when...
-
3
Good Ruby Idiom: and/or operators Feb 23, 2016 Any tutorial and book will teach you there are two sets of similar operators in Ruby: &&/|| vs and/or (and also
-
6
pImpl 技巧:接口与实现分离 2020.03.12 Netcan 编程
-
9
an alternative to Pimpl The C++ Priv idiom: an alternative to Pimpl by Yair Chuchem 2021.03.09 C++ is infamous for long compilatio...
-
8
Please repeat yourself: The noexcept(noexcept(…)) idiom Raymond C April 8th, 2022 Consider the following class:...
-
6
The copy and swap idiom in C++ Sandor Dargo 21 hours ago2022-08-10T00:00:00+02:00Last year, as the usage of our services grew sometimes by 20 times, we had to spend significant efforts on optimizing our appl...
-
5
设计 C++ 接口文件的小技巧之 PIMPL
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK