Итак, мне интересно, эта техника действительно используется на практике? Я должен использовать это везде или с осторожностью?
Конечно это используется. Я использую это в своем проекте, почти в каждом классе.
Причины использования идиомы 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, Это кажется мне более естественным. Есть ли какая-то другая проблема, которую я пропустил?