Итак, мне интересно, эта техника действительно используется на практике? Я должен использовать это везде или с осторожностью?
Конечно это используется. Я использую это в своем проекте, почти в каждом классе.
Причины использования идиомы PIMPL:
Бинарная совместимость
Когда вы разрабатываете библиотеку, вы можете добавлять / изменять поля, XImpl
не нарушая бинарную совместимость с вашим клиентом (что будет означать сбои!). Поскольку двоичный макет X
класса не изменяется при добавлении новых полей в Ximpl
класс, безопасно добавлять новые функции в библиотеку при обновлении второстепенных версий.
Конечно, вы также можете добавлять новые публичные / частные не виртуальные методы в X
/ XImpl
без нарушения бинарной совместимости, но это на уровне стандартной техники заголовка / реализации.
Скрытие данных
Если вы разрабатываете библиотеку, особенно проприетарную, может быть желательно не раскрывать, какие другие библиотеки / методы реализации использовались для реализации открытого интерфейса вашей библиотеки. Либо из-за проблем с интеллектуальной собственностью, либо потому, что вы считаете, что у пользователей может возникнуть соблазн принять опасные предположения относительно реализации или просто нарушить инкапсуляцию, используя ужасные приемы приведения. PIMPL решает / смягчает это.
Время компиляции
Время компиляции уменьшается, поскольку X
при добавлении / удалении полей и / или методов к XImpl
классу необходимо перестраивать только исходный файл (файл реализации), который сопоставляется с добавлением приватных полей / методов в стандартной технике). На практике это обычная операция.
При использовании стандартного метода заголовка / реализации (без PIMPL) при добавлении нового поля X
каждый клиент, который когда-либо выделяет X
(либо в стеке, либо в куче), должен быть перекомпилирован, поскольку он должен регулировать размер выделения. Хорошо, каждый клиент, который никогда не выделяет X, также должен быть перекомпилирован, но это просто накладные расходы (результирующий код на стороне клиента будет таким же).
Более того, со стандартным разделением заголовок / реализация XClient1.cpp
необходимо перекомпилировать, даже если частный метод X::foo()
был добавлен X
и X.h
изменен, даже если он XClient1.cpp
не может вызывать этот метод по причинам инкапсуляции! Как и выше, это просто накладные расходы и связано с тем, как работают реальные системы сборки C ++.
Конечно, перекомпиляция не нужна, когда вы просто изменяете реализацию методов (потому что вы не трогаете заголовок), но это на уровне стандартной техники заголовка / реализации.
Рекомендуется ли использовать эту технику во встроенных системах (где производительность очень важна)?
Это зависит от того, насколько сильна ваша цель. Тем не менее, единственный ответ на этот вопрос: измерить и оценить, что вы получаете и теряете. Кроме того, примите во внимание, что если вы не публикуете библиотеку, предназначенную для использования во встроенных системах вашими клиентами, применяется только преимущество времени компиляции!
struct XImpl : public X
, Это кажется мне более естественным. Есть ли какая-то другая проблема, которую я пропустил?