Сокрытие реализации с помощью PIMPL
Скрывайте детали реализации за идиомой указателя на реализацию.
«Сокрытие реализации с помощью PIMPL» — бесплатный урок C++ Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Идиома PIMPL
PIMPL — «указатель на реализацию». Переместите закрытые данные класса в отдельную структуру Impl, доступ к которой осуществляется через указатель.
Зачем нужен PIMPL
Преимущества:
- Скрывает детали реализации от заголовков
- Уменьшает зависимости во время компиляции
- Сохраняет стабильность ABI при добавлении закрытых членов
- Позволяет заменять реализации
Базовая структура
Общедоступный класс содержит указатель на структуру Impl, определённую только в файле .cpp.
// widget.h
class Widget {
public:
Widget();
~Widget();
void doSomething();
private:
struct Impl;
std::unique_ptr<Impl> pimpl_;
};Impl в .cpp
Структура Impl содержит все фактические данные.
// widget.cpp
struct Widget::Impl {
std::string name;
int counter;
// ... anything else, freely
};
Widget::Widget()
: pimpl_(std::make_unique<Impl>()) {}
Widget::~Widget() = default; // must be in .cpp where Impl is complete
void Widget::doSomething() {
pimpl_->counter++;
}Деструктор в cpp
Деструктор необходимо определить в файле .cpp, где Impl является полным типом. Иначе unique_ptr не сможет удалить неполный тип.
Специальные функции-члены
При необходимости также следует явно объявить копирование, перемещение и присваивание. Версии, сгенерированные компилятором, не могут работать с неполным типом в заголовке.
// widget.h
class Widget {
public:
Widget(const Widget& other);
Widget& operator=(const Widget& other);
Widget(Widget&&) noexcept;
Widget& operator=(Widget&&) noexcept;
~Widget();
};Накладные расходы
PIMPL добавляет косвенное обращение при каждом доступе к члену и выделение памяти в куче. Для часто вызываемых методов это может иметь значение — обязательно измерьте производительность.
Ускорение сборки
Заголовки, которые раньше подключали типы стандартной библиотеки или ресурсоёмкие зависимости, теперь могут быть компактными. Изменяйте .cpp без перекомпиляции кода потребителей.
PIMPL и наследование
Его можно сочетать с наследованием, но это усложняет конструкцию. Общедоступный класс обычно ни от чего не наследуется; Impl может свободно использовать наследование.
Варианты
Альтернативы и усовершенствования:
- Быстрый PIMPL — встроенное хранение с использованием размещающего оператора new
- Стабильный адрес — Impl остаётся выделенным на всё время жизни объекта
- Стирание типа — Impl может быть полиморфным
Использование в реальных проектах
PIMPL широко используется в:
- Qt (каждый виджет использует PIMPL)
- Многих проприетарных библиотеках
- Boost (в некоторых местах)
Когда использовать
Используйте PIMPL, если:
- Вам нужна стабильность ABI между версиями
- Ваши заголовки транзитивно подключают слишком много зависимостей
- Вы предоставляете общедоступный API библиотеки
Когда не использовать
Не используйте PIMPL, если:
- Класс является внутренним — его заголовок не является зависимостью потребителей
- Накладные расходы на производительность важнее времени сборки
- Класс небольшой или изменяется редко
Быстрая проверка
Почему деструктор класса с PIMPL необходимо определить в файле .cpp?
Итоги
PIMPL скрывает реализацию, перемещая закрытые данные в структуру Impl, доступ к которой осуществляется через unique_ptr. Это повышает стабильность ABI и ускоряет сборку, но добавляет косвенное обращение. Определяйте специальные функции-члены в .cpp, где Impl является полным типом.
Часто задаваемые вопросы
Урок «Сокрытие реализации с помощью PIMPL» бесплатный?
Да — полный текст урока «Сокрытие реализации с помощью PIMPL» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «Сокрытие реализации с помощью PIMPL»?
Скрывайте детали реализации за идиомой указателя на реализацию. Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Сокрытие реализации с помощью PIMPL»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Библиотеки только из заголовков и скомпилированные библиотеки
- Версионирование API и стабильность ABI
- Сокрытие реализации с помощью PIMPL
- Создание современной библиотеки утилит только из заголовков