What is the PIMPL idiom in C++?
Answer
The PIMPL idiom (Pointer to Implementation, also Opaque Pointer, Compilation Firewall) hides implementation details from the class interface, reducing compilation dependencies and enabling binary compatibility. Problem without PIMPL: any change to private members in a header file forces recompilation of all files that include that header — even if the public API didn't change. PIMPL solution: // widget.h (public interface -- stable): class Widget { public: Widget(); ~Widget(); // Must be defined where Impl is complete Widget(Widget&&) noexcept; Widget& operator=(Widget&&) noexcept; void doSomething(); int getValue() const; private: struct Impl; // Forward declaration -- Impl is hidden std::unique_ptr<Impl> pImpl; // Pointer to hidden impl }; // widget.cpp (implementation -- can change freely): #include "widget.h" #include <vector> // Heavy headers only in .cpp! #include "heavy_library.h" struct Widget::Impl { std::vector<int> data; HeavyLibraryClass helper; int cachedValue = 0; void expensiveCalculation() { /* ... */ } }; Widget::Widget() : pImpl(std::make_unique<Impl>()) {} Widget::~Widget() = default; // Must be in .cpp where Impl is complete! Widget::Widget(Widget&&) noexcept = default; Widget& Widget::operator=(Widget&&) noexcept = default; void Widget::doSomething() { pImpl->expensiveCalculation(); } int Widget::getValue() const { return pImpl->cachedValue; }. Benefits: (1) Include heavy dependencies only in .cpp (faster builds); (2) Changes to Impl don't require recompiling users of Widget; (3) ABI stability — binary interface remains the same even when implementation changes; (4) Hide implementation details from users (IP protection). Costs: extra heap allocation (unique_ptr); one indirection per member access; must implement destructor, move ctor/assignment in .cpp. Used by: Qt's d-pointer, many library APIs requiring stable ABI.