Мой личный стиль в C ++ всегда заключался в том, чтобы помещать объявления классов во включаемый файл, а определения - в файл .cpp, очень похоже на то, как это предусмотрено в ответе Локи на файлы заголовков C ++, разделение кода . По общему признанию, одна из причин, по которой мне нравится этот стиль, вероятно, связана со всеми годами, которые я потратил на программирование Modula-2 и Ada, оба из которых имеют похожую схему с файлами спецификаций и файлами тела.
У меня есть коллега, гораздо более осведомленный в C ++, чем я, который настаивает на том, чтобы все объявления C ++ по возможности включали определения прямо в заголовочный файл. Он не говорит, что это допустимый альтернативный стиль или даже немного лучший стиль, а скорее это новый общепринятый стиль, который все сейчас используют для C ++.
Я не такой подвижный, как раньше, поэтому я не очень-то стремлюсь заскочить на его подножку, пока не увижу с ним еще несколько человек. Так насколько распространена эта идиома на самом деле?
Просто, чтобы дать некоторую структуру ответам: «Это путь» , очень распространенный, довольно распространенный, необычный или сумасшедший?
Ответы:
Ваш коллега не прав, распространенным способом является и всегда помещать код в файлы .cpp (или любое другое расширение, которое вам нравится) и объявления в заголовках.
Иногда есть смысл помещать код в заголовок, это может позволить более умному встраиванию компилятором. Но в то же время это может разрушить время компиляции, поскольку весь код должен обрабатываться каждый раз, когда он включается компилятором.
Наконец, часто раздражает наличие циклических объектных отношений (иногда желаемых), когда весь код является заголовками.
Итог, вы были правы, он не прав.
РЕДАКТИРОВАТЬ: я думал о вашем вопросе. Есть один случай, когда то, что он говорит, правда. шаблоны. Многие новые «современные» библиотеки, такие как boost, интенсивно используют шаблоны и часто являются «только заголовками». Однако это следует делать только при работе с шаблонами, поскольку это единственный способ сделать это при работе с ними.
РЕДАКТИРОВАТЬ: Некоторые люди хотели бы немного больше разъяснений, вот некоторые мысли о минусах написания кода "только заголовок":
Если вы будете искать по всему, вы увидите, что довольно много людей пытаются найти способ сократить время компиляции при работе с boost. Например: Как сократить время компиляции с Boost Asio , который видит компиляцию 14 с одного файла 1K с включенным Boost . Может показаться, что 14-ые не «взрываются», но они, безусловно, намного дольше, чем обычно, и могут складываться довольно быстро. При работе с крупным проектом. Библиотеки только с заголовками действительно влияют на время компиляции. Мы просто терпим это, потому что повышение очень полезно.
Кроме того, есть много вещей, которые нельзя сделать только в заголовках (даже в boost есть библиотеки, на которые нужно ссылаться для определенных частей, таких как потоки, файловая система и т. Д.). Основным примером является то, что вы не можете иметь простые глобальные объекты в заголовках только libs (если вы не прибегаете к мерзости, которая является одиночной), так как вы столкнетесь с множественными ошибками определения. ПРИМЕЧАНИЕ: встроенные переменные C ++ 17 сделают этот конкретный пример выполнимым в будущем.
И наконец, при использовании boost в качестве примера кода, содержащего только заголовки, часто пропускаются огромные детали.
Boost - это библиотека, а не код пользовательского уровня. так что это не так часто меняется. В пользовательском коде, если вы помещаете все в заголовки, каждое небольшое изменение заставит вас перекомпилировать весь проект. Это огромная трата времени (и это не относится к библиотекам, которые не переходят от компиляции к компиляции). Когда вы делите вещи между заголовком / источником и, еще лучше, с помощью предварительных объявлений, чтобы уменьшить количество включений, вы можете сэкономить часы перекомпиляции при суммировании в течение дня.
источник
В тот день, когда программисты на С ++ договорились о «Пути» , ягнята присядет со львами, палестинцы примут израильтян, а кошкам и собакам разрешат жениться.
Разделение между файлами .h и .cpp в большинстве случаев является произвольным, пережитком давно прошедших оптимизаций компилятора. На мой взгляд, объявления принадлежат заголовку, а определения принадлежат файлу реализации. Но это просто привычка, а не религия.
источник
Код в заголовках, как правило, плохая идея, так как он вызывает перекомпиляцию всех файлов, содержащих заголовок, когда вы изменяете фактический код, а не объявления. Это также замедлит компиляцию, так как вам нужно будет анализировать код в каждом файле, который содержит заголовок.
Причина наличия кода в заголовочных файлах заключается в том, что обычно ключевое слово inline должно работать должным образом и при использовании шаблонов, которые создаются в других файлах cpp.
источник
Что может быть полезным для вас, коллега, так это представление о том, что большая часть кода C ++ должна быть спроектирована так, чтобы обеспечить максимальное удобство использования. И если он является шаблонным, то все должно быть в заголовочном файле, чтобы клиентский код мог его увидеть и создать экземпляр. Если это достаточно хорошо для Boost и STL, это достаточно хорошо для нас.
Я не согласен с этой точкой зрения, но это может быть то, откуда это исходит.
источник
Я думаю, что ваш коллега умен и вы тоже правы.
Полезные вещи, которые я обнаружил, помещают все в заголовки так:
Нет необходимости писать и синхронизировать заголовки и источники.
Структура проста, и никакие циклические зависимости не заставляют кодера создавать «лучшую» структуру.
Портативный, легко встраивается в новый проект.
Я согласен с проблемой времени компиляции, но я думаю, что мы должны заметить, что:
Смена исходного файла, скорее всего, изменит заголовочные файлы, что приведет к повторной компиляции всего проекта.
Скорость компиляции намного быстрее, чем раньше. И если у вас есть проект, который будет создаваться с длительной и высокой частотой, это может указывать на недостатки вашего проекта. Разделите задачи на разные проекты, и модуль может избежать этой проблемы.
Наконец, я просто хочу поддержать твоего коллегу, просто по моему личному мнению.
источник
Часто я помещаю тривиальные функции-члены в заголовочный файл, чтобы позволить им быть встроенными. Но чтобы поместить все тело кода, просто чтобы соответствовать шаблонам? Это просто орехи.
Помните: глупая последовательность - это хобгоблин маленьких умов .
источник
A<B>
в файле cpp, а затем пользователь хочет получитьA<C>
?Как сказал Туомас, ваш заголовок должен быть минимальным. Для полноты я немного расширю.
Я лично использую 4 типа файлов в своих
C++
проектах:Кроме того, я связываю это с другим правилом: не определяйте, что вы можете объявить. Хотя, конечно, я разумен там (использование Pimpl везде довольно хлопотно).
Это означает, что я предпочитаю предварительную декларацию перед
#include
директивой в моих заголовках всякий раз, когда я могу сойти с рук.Наконец, я также использую правило видимости: я ограничиваю области своих символов в максимально возможной степени, чтобы они не загрязняли внешние области.
В целом
Спасатель здесь является то , что большую часть времени вперед заголовок бесполезен: необходимо только в случае ,
typedef
илиtemplate
и так заголовок реализации;)источник
Чтобы добавить больше удовольствия, вы можете добавить
.ipp
файлы, которые содержат реализацию шаблона (которая включена в.hpp
), в то время как.hpp
содержит интерфейс.Помимо шаблонного кода (в зависимости от проекта это может быть большинство или меньшая часть файлов), существует нормальный код, и здесь лучше разделить объявления и определения. При необходимости укажите также предварительные объявления - это может повлиять на время компиляции.
источник
Как правило, при написании нового класса я помещаю весь код в класс, поэтому мне не нужно искать его в другом файле. После того, как все работает, я разбиваю тело методов в файл cpp. оставив прототипы в файле hpp.
источник
Если этот новый путь действительно The Way , мы могли бы столкнуться в другом направлении в наших проектах.
Потому что мы стараемся избегать всех ненужных вещей в заголовках. Это включает в себя избегание каскада заголовков. Код в заголовках, вероятно, будет нуждаться во включении другого заголовка, который будет нуждаться в другом заголовке и так далее. Если мы вынуждены использовать шаблоны, мы стараемся избегать засорения заголовков шаблоном.
Также мы используем шаблон «непрозрачный указатель», когда это применимо.
С помощью этих методов мы можем делать более быстрые сборки, чем большинство наших коллег. И да ... изменение кода или членов класса не приведет к огромным перестройкам.
источник
Я лично делаю это в моих заголовочных файлах:
Мне не нравится смешивать код для методов с классом, так как мне сложно быстро все искать.
Я бы не помещал ВСЕ методы в заголовочный файл. Компилятор (обычно) не сможет встроить виртуальные методы и (вероятно) только встроенные небольшие методы без циклов (полностью зависит от компилятора).
Использование методов в классе допустимо ... но с точки зрения читабельности мне это не нравится. Помещение методов в заголовок означает, что, когда это возможно, они будут встроены.
источник
ИМХО, он достоин ТОЛЬКО если он занимается шаблонами и / или метапрограммированием. Уже упоминалось множество причин, по которым вы ограничиваете заголовочные файлы только объявлениями. Они просто ... заголовки. Если вы хотите включить код, вы компилируете его как библиотеку и связываете его.
источник
Я положил всю реализацию из определения класса. Я хочу, чтобы комментарии doxygen были вне определения класса.
источник
Я думаю, что абсолютно абсурдно помещать ВСЕ ваши определения функций в заголовочный файл. Зачем? Потому что заголовочный файл используется как интерфейс PUBLIC для вашего класса. Это за пределами «черного ящика».
Когда вам нужно посмотреть на класс, чтобы узнать, как его использовать, вы должны посмотреть на заголовочный файл. Заголовочный файл должен давать список того, что он может делать (закомментированный, чтобы описать детали того, как использовать каждую функцию), и он должен включать список переменных-членов. Он НЕ ДОЛЖЕН включать КАК реализована каждая отдельная функция, потому что это лишний груз ненужной информации и только загромождает заголовочный файл.
источник
Разве это не зависит от сложности системы и внутренних соглашений?
В данный момент я работаю над симулятором нейронной сети, который невероятно сложен, и приемлемый стиль, который я должен использовать:
Определения классов в classname.h
Код класса в classnameCode.h
исполняемый код в classname.cpp
Это отделяет пользовательское моделирование от базовых классов, разработанных разработчиком, и лучше всего работает в данной ситуации.
Тем не менее, я был бы удивлен, увидев, что люди делают это, скажем, в графическом приложении или любом другом приложении, целью которого не является предоставление пользователям базы кода.
источник
Код шаблона должен быть только в заголовках. Кроме того, все определения, кроме строк, должны быть в .cpp. Лучшим аргументом для этого были бы реализации библиотеки std, которые следуют тому же правилу. Вы не согласитесь, что разработчики std lib будут правы в этом отношении.
источник
libstdc++
Похоже, что GCC (AFAICS) почти ничего не вставляетsrc
и почти всеinclude
, независимо от того, должно ли это быть в заголовке. Поэтому я не думаю, что это точная / полезная цитата. В любом случае, я не думаю, что stdlibs - это большая часть модели для пользовательского кода: они, очевидно, написаны высококвалифицированными программистами, но для использования , а не для чтения: они абстрагируются от высокой сложности, о которой большинству кодеров не нужно думать Нужно безобразно_Reserved
__names
везде, чтобы избежать конфликтов с пользователем, комментарии и интервалы ниже того, что я бы посоветовал, и т. д. Они являются образцовыми в узком смысле.Я думаю, что ваш коллега прав, если он не входит в процесс, чтобы написать исполняемый код в заголовке. Правильный баланс, я думаю, должен следовать по пути, указанному в GNAT Ada, где файл .ads дает совершенно адекватное определение интерфейса пакета для его пользователей и его дочерних элементов.
Кстати, Тед, вы смотрели на этом форуме недавний вопрос о привязке Ada к библиотеке CLIPS, которую вы написали несколько лет назад и которая больше не доступна (соответствующие веб-страницы теперь закрыты). Даже если она сделана для старой версии Clips, эта привязка может стать хорошим примером для тех, кто хочет использовать механизм вывода CLIPS в программе Ada 2012.
источник