C++C++ Explainer
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
changeeffect
add a memberevery includer rebuilds
headerpulls heavy deps
ABIbreaks
→
PIMPL
changeeffect
add a memberone .cpp rebuilds
headerforward decls only
ABIstable
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.