PIMPLL
Pimpl (Pointer to IMPLementation, указатель на реализацию) - это популярный паттерн проектирования в C++, который отделяет интерфейс класса от его реализации.
Преимущества:
- Сокрытие реализации: PIMPL скрывает закрытые члены данных в отдельном классе (в модуле
.cpp), на который указывает указатель в интерфейсном классе (в заголовочном файле.h). - ABI-совместимость: Позволяет обновлять внутреннюю логику модуля или библиотеки без нарушения совместимости двоичного кода.
- Сокращение времени компиляции: Изменения в закрытых членах данных требуют перекомпиляции только файла реализации (
.cpp), а не всех заголовочных файлов (.h), зависящих от него. - Упрощение логики move-семантики и устойчивость к исключениям: Поскольку все данные сосредоточены в одном единственном указателе
std::unique_ptr<Impl>, автоматически сгенерированный или написанный вручную move-конструктор становится тривиальным — нужно передать владение всего одним указателем. Это идиоматично и безопасно с точки зрения исключений.
Недостатки:
- Расход памяти: Дополнительное выделение памяти в куче (new/delete).
- Вызовы через косвенность: Каждый доступ к приватному полю - это переход по указателю (pointer indirection), что может быть немного медленнее.
- Многословность: Приходится писать класс-обертку, что увеличивает количество кода.
Пример:
// widget.h
#include <memory>
class Widget {
public:
Widget();
~Widget();
void draw();
private:
struct Impl; // предварительное объявление структуры реализации
std::unique_ptr<Impl> pImpl; // указатель на реализацию
};
// widget.cpp
#include "widget.h"
#include <iostream>
struct Widget::Impl {
void drawImpl() {
std::cout << "Drawing Widget..." << std::endl;
}
};
Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
// деструктор должен быть определен после полного определения Impl
Widget::~Widget() = default;
void Widget::draw() {
pImpl->drawImpl();
}