C++ совет от Code Complete по инкапсуляции?
В разделе "Надлежащая инкапсуляция" в Code Complete рекомендуется скрыть подробности частной реализации. Пример приведен в C++. Идея состоит в том, чтобы полностью отделить интерфейс от реализации, даже на уровне класса.
class Employee {
public:
...
Employee( ... );
...
FullName GetName() const;
String GetAddress() const;
private:
EmployeeImplementation *m_implementation;
};
Это действительно хорошее использование времени? Мало того, что это кажется неэффективным (какой штраф за производительность это даст?), Но и весь девиз Code Complete ("управление сложностью"), похоже, полностью изменен - разве это не добавляет сложности?
5 ответов
Другое преимущество идиомы PIMPL может заключаться в поддержании ABI. Посмотрите Идиому Пимпл на практике.
Размер класса остается постоянным. Это означает, что вы можете изменить внутреннюю реализацию, оставив интерфейс без изменений.
Если реализация распространяется в скомпилированной форме (lib, dll и т. Д.), То при некоторых условиях вы можете просто заменить библиотеку без необходимости перекомпилировать код, который использует класс. Таким образом, вы развязываете код как полностью автономный модуль, если публичный интерфейс не изменяется.
Как утверждают другие, это также уменьшает время компиляции, что может быть достаточной причиной в некоторых случаях.
Что ж, это увеличивает инкапсуляцию, поскольку ваш заголовочный файл теперь содержит только открытые члены и один указатель на частную реализацию.
Это также (немного?) Снижает производительность из-за дополнительного уровня косвенности.
"Сокращает время компиляции" является ключевым вопросом здесь.
Если вы (ваша компания) являетесь единственными пользователями в классе, то я не думаю, что у вас есть какие-либо причины использовать эту идиому. Вы получаете более низкую производительность, и вам все равно придется ежедневно (или периодически) перестраивать ваш исходный код (который должен учитывать зависимости между классами).
Это означает, что время компиляции должно быть в значительной степени не имеет значения, если вы являетесь единственным потребителем класса.
Если вы распространяете библиотеку, то история совершенно другая. Изменения в заголовках означают, что любые ваши клиенты должны будут перестроить свои приложения для использования вашей новой версии, даже если вы изменили приватные части класса. Использование здесь идиомы pimpl означало бы, что изменение невидимо для пользователей вашей динамической библиотеки.
Эта идиома используется для абстрагирования от плохих заголовков, и не более того. Используйте его только в том случае, если типы, требуемые для определения класса, включают в себя заголовки, которые пропускают макросы, занимают много времени для компиляции и т. Д. Кроме того, это обычно не считается правильным решением. Так как ваша реализация в любом случае требует динамического размещения и ссылочной семантики, вы можете просто сделать ее интерфейсом и предложить CreateForMyPlatform()
метод, который имеет определение в файле cpp. По крайней мере, вы можете использовать умные указатели для этого сценария.
Я думаю, что главное преимущество (или, по крайней мере, одного из них) идиомы pimpl заключается не в экономии времени компиляции, а в возможности свободной связи, то есть нарушения зависимостей между компонентами.
Предположим, вы предоставляете библиотеку инфраструктуры, которую используют многие другие компоненты. Затем, как указывало @zvrba, каждый раз, когда вы изменяете детали своей частной реализации, все ваши клиенты должны перекомпилировать. Это может быть не сложно, но в больших и сложных проектах интеграция компонентов может быть сложной задачей. С pimpl, если ваша библиотека динамическая (dll, .so), то ваши клиенты не требуют никаких действий.
Дополнительный уровень косвенности через указатель может вызвать дополнительные пропуски в кеше и замедлить вашу программу. AFAIK, эта идиома (PIMPL) чаще всего предлагается для сокращения времени компиляции. Скажи, что у тебя есть employee.h
заголовок, имеющий все поля в классе, вместо одного указателя. Теперь, когда вы меняете данные сотрудника (например, добавляете или удаляете поле), КАЖДЫЙ файл включает employee.h
должен быть перекомпилирован. Если все, что у вас есть, это один указатель на класс реализации, определенный в employee.cpp
тогда ТОЛЬКО employee.cpp
должен быть перекомпилирован при изменении EmployeeImplementation
,
Теперь стоит ли сокращенное время компиляции за дополнительную плату? Только вы можете решить это.