Interfaces
I.27
Для стабильного ABI библиотеки рассмотрите идиому Pimpl
Причина
Поскольку закрытые данные-члены участвуют в схеме класса, а закрытые функции-члены участвуют в разрешении перегрузки, изменения в этих деталях реализации требуют перекомпиляции всех пользователей класса, использующих их. Не-полиморфный интерфейсный класс, хранящий указатель на реализацию (Pimpl), может изолировать пользователей класса от изменений в его реализации ценой одного косвенного обращения.
Пример
интерфейс (widget.h)
class widget {
class impl;
std::unique_ptr<impl> pimpl;
public:
void draw(); // публичный API, который будет делегирован реализации
widget(int); // определён в файле реализации
~widget(); // определён в файле реализации, где impl — полный тип
widget(widget&&) noexcept; // определён в файле реализации
widget(const widget&) = delete;
widget& operator=(widget&&) noexcept; // определён в файле реализации
widget& operator=(const widget&) = delete;
};
реализация (widget.cpp)
class widget::impl {
int n; // закрытые данные
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 для ознакомления с компромиссами и дополнительными деталями реализации этой идиомы.
Контроль
(Не применяется) Надёжно определить, где интерфейс является частью ABI, затруднительно.