Каковы преимущества и недостатки реализации классов в заголовочных файлах?
Мне нравится концепция DRY (не повторяйте себя [упс]), но концепция заголовочных файлов в C++ противоречит этому правилу программирования. Есть ли какой-либо недостаток для определения члена класса полностью в заголовке? Если это правильно для шаблонов, почему не для обычных классов? У меня есть несколько идей по поводу недостатков и преимуществ, но какие у вас?
6 ответов
Возможные преимущества размещения всего в заголовочных файлах:
- Меньшая избыточность (что приводит к более легким изменениям, упрощению рефакторинга и т. Д.)
- Может дать компилятору / компоновщику лучшие возможности для оптимизации
- Часто проще включить в существующий проект
Возможные недостатки размещения всего в заголовочных файлах:
- Более длинные циклы компиляции / компоновки
- Потеря разделения интерфейса и реализации
- Может привести к трудно разрешаемым круговым зависимостям
- Много встраивания может увеличить размер исполняемого файла
- Предотвращает двоичную совместимость разделяемых библиотек /DLL
- Расстраивает коллег, которые предпочитают традиционные способы использования C++
Что ж, одна проблема состоит в том, что реализации обычно меняются гораздо чаще, чем определения классов, поэтому для большого проекта вам придется перекомпилировать мир для каждого небольшого изменения.
Ты не повторяешься. Вы пишете код только один раз в одном заголовке. Это повторяется препроцессором, но это не ваша проблема, и это не является нарушением DRY.
Если это правильно для шаблонов, почему не для обычных классов
Это не совсем то, что нужно делать с шаблонами. Это единственный, который действительно работает в целом.
В любом случае, если вы реализуете класс в заголовке, вы получаете следующие преимущества и недостатки:
- Полная реализация видна везде, где она используется, что упрощает компиляцию при необходимости.
- Один и тот же код будет проанализирован и скомпилирован несколько раз, что приведет к увеличению времени компиляции.
- С другой стороны, если все находится в заголовках, это может привести к меньшему количеству блоков перевода, и поэтому компилятор должен запускаться меньше раз. В конечном итоге, у вас может получиться один модуль перевода, который включает все сразу, что может привести к очень быстрой компиляции.
И... вот и все, правда.
Большая часть моего кода имеет тенденцию быть в заголовках, но это потому, что большая часть моего кода - шаблоны.
Основная причина не реализовывать класс в заголовочном файле: нужно ли потребителям вашего класса знать детали его реализации? Ответ почти всегда нет. Они просто хотят знать, какой интерфейс они могут использовать для взаимодействия с классом. Наличие реализации класса, видимой в заголовке, значительно затрудняет понимание этого интерфейса.
Помимо соображений компактности и отделения интерфейса от реализации, есть и коммерческие мотивы. Если вы разрабатываете библиотеку для продажи, вы (вероятно) не хотите раскрывать детали реализации библиотеки, которую вы продаете.
Основным недостатком (кроме длинных сборок) является отсутствие четкого разделения интерфейса и реализации.
В идеале вам не нужно видеть реализацию интуитивно понятного и хорошо документированного интерфейса.
Пока не упомянуто: виртуальные функции создаются для каждого включения, так что вы можете раздуть свой исполняемый файл (я не уверен, верно ли это для всех компиляторов).
Есть альтернатива:
Делайте много вещей в классах, объявленных в вашем исходном файле. 1 пример - это pimpl-idiom, но есть также люди, которые боятся объявлять классы из заголовочного файла. Тем не менее, это имеет смысл для частных классов.