C++ · 7 · Advanced / Job-Ready32 / 36 · 89%
PIMPL & Compile-Time Firewalls
Hide the implementation, cut rebuild times, keep the ABI stable.
Examples: unique_ptr<Impl>, forward declaration
shortcuts: ← prev · → next · M mark
1
The Header
Only a forward declaration and an owning pointer are visible.
Example
example
// widget.hpp
class Widget {
public:
Widget();
~Widget(); // must be declared here
Widget(Widget&&) noexcept;
Widget& operator=(Widget&&) noexcept;
void draw();
private:
struct Impl;
std::unique_ptr<Impl> p_;
};2
The Source
The real members live in the .cpp, invisible to every caller.
Example
example
// widget.cpp
struct Widget::Impl {
std::vector<Shape> shapes;
Renderer renderer;
};
Widget::Widget() : p_(std::make_unique<Impl>()) {}
Widget::~Widget() = default; // Impl is complete HERE
void Widget::draw() { p_->renderer.draw(p_->shapes); }WATCH OUT
Declare the destructor in the header and define it (= default) in the .cpp. Otherwise unique_ptr tries to delete an incomplete type.
3
What You Gain
Change internals without recompiling the world.
Plain class
| change | effect |
|---|---|
| add a member | every includer rebuilds |
| header | pulls heavy deps |
| ABI | breaks |
PIMPL
| change | effect |
|---|---|
| add a member | one .cpp rebuilds |
| header | forward decls only |
| ABI | stable |
4
The Cost
Not free — measure before applying it everywhere.
One heap allocation per object and an extra indirection on every member access. Use it for library boundaries and heavy headers, not for small value types.