Pimpl ẩn chi tiết cài đặt của class vào một private struct — trỏ bằng unique_ptr — chỉ khai báo forward declaration trong header.
// Widget.h — header thuần "interface"
class Widget {
public:
Widget();
~Widget();
void doWork();
private:
struct Impl; // forward declaration
std::unique_ptr<Impl> p_;
};
// Widget.cpp — chi tiết ẩn trong .cpp
struct Widget::Impl {
HeavyResource res; // type này không cần expose trong header
};
Widget::Widget() : p_(std::make_unique<Impl>()) {}
void Widget::doWork() { p_->res.process(); }Lợi ích:
- ABI stability: thay đổi Impl không cần recompile code dùng Widget.h.
- Compilation firewall: header nhẹ, không kéo vào các #include nặng.
- Encapsulation thực sự: private members trong header vẫn lộ tên kiểu — pimpl ẩn hoàn toàn.
Lưu ý: destructor phải được định nghĩa trong .cpp (nơi Impl complete) — không để compiler gen mặc định ở header.
Pimpl hides implementation details of a class inside a private struct — pointed to by unique_ptr — with only a forward declaration in the header.
// Widget.h — pure interface header
class Widget {
public:
Widget();
~Widget();
void doWork();
private:
struct Impl; // forward declaration
std::unique_ptr<Impl> p_;
};
// Widget.cpp — details hidden in .cpp
struct Widget::Impl {
HeavyResource res; // this type doesn't leak into the header
};
Widget::Widget() : p_(std::make_unique<Impl>()) {}
void Widget::doWork() { p_->res.process(); }Benefits:
- ABI stability: changing Impl doesn't require recompiling code that includes Widget.h.
- Compilation firewall: lightweight header, no heavy #include transitive pull-in.
- True encapsulation: private members in headers still expose type names — pimpl hides everything.
Note: destructor must be defined in the .cpp (where Impl is complete) — do not let the compiler generate it in the header.