⚙️ C++ Advanced

What is the PIMPL idiom in C++?

Why Interviewers Ask This

This is a differentiating question used for senior and lead roles. Interviewers want to see if you can explain not just what happens, but why — and what the trade-offs are in different approaches.

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.

Pro Tip

Before answering, structure your response: one-line definition → real-world analogy → concrete example from a project. This makes even complex C++ answers easy to follow.