02 April 2022

C++ 核心指南目录

I.27: For stable library ABI, consider the Pimpl idiom

理由

因为私有成员变量构成类的内部布局,私有成员函数则参与函数重载解析,修改了类的底层实现,就要求重新编译使用该类的所有文件。

通过让一个非多态的接口保管一个指向实现的指针( Pimpl ),就可以分离代码实现的变动。付出的代价就是无法直接访问底层的实现。

例子

接口 (widget.h)

class widget {
    class impl;
    std::unique_ptr<impl> pimpl;
public:
    void draw(); // public API that will be forwarded to the implementation
    widget(int); // defined in the implementation file
    ~widget();   // defined in the implementation file,
                 // where impl is a complete type
    widget(widget&&) noexcept; // defined in the implementation file
    widget(const widget&) = delete;
    widget& operator=(widget&&) noexcept; // defined in the implementation file
    widget& operator=(const widget&) = delete;
};

widget的底层实现 (widget.cpp)

class widget::impl {
    int n; // private data
public:
    void draw(const widget& w) { /* ... */ }
    impl(int n) : n(n) {}
};
void widget::draw() { pimpl->draw(*this); }
widget::widget(int n) : pimpl{std::make_unique<impl>(n)} {}
widget::widget(widget&&) noexcept = default;
widget::~widget() = default;
widget& widget::operator=(widget&&) noexcept = default;

注意

更多实现细节 GOTW #100 cppreference