Размещение методов в файлах .h или .cpp - PullRequest
3 голосов
/ 08 декабря 2010

Я видел код для класса, помещенного в отдельный C ++, а определения методов помещены в заголовочный файл.Мой первый опыт ООП связан с Java, в котором все методы помещены в файл класса, и я на самом деле предпочитаю это.

Влияет ли размещение всех моих методов в заголовочном файле на код сборки, сгенерированный компилятором, или нет?

И если да, то вообще ли вредно для производительности помещать весь код класса в заголовочный файл?

Ответы [ 3 ]

6 голосов
/ 08 декабря 2010

Дело в том, что сложные программы на C ++ создаются путем компиляции нескольких объектов, а затем их соединения. Каждый объект обычно является результатом компиляции одного файла реализации (например, «.cpp», «.cc» и т. Д.), Который может прямо или косвенно включать в себя множество заголовков. Следовательно, если вы напишите хороший класс и поместите код в заголовок, то этот код может быть включен в несколько объектных файлов, а затем компилятор генерирует его избыточно, и далее - компоновщик не будет (и не может легко) сравнивать версии, чтобы увидеть, являются ли они эквивалентными, и удалить избыточные копии (проще, если использовать относительные адреса - «позиционно-независимый код» - но это уже другая история). Смотрите также комментарий Джальфа ниже.

Таким образом, вы не хотите, чтобы в ваших заголовках были разные функции вне строки. Если они являются номинально inline функциями - из-за использования ключевого слова inline или определения внутри класса - тогда компилятору просто придется выполнить дополнительную работу и убедиться, что любая их внеплановая версия представлена ​​уникальным образом. в исполняемом файле. Но для функций вне линии бремя остается за программистом.

Кроме того, если вы предоставляете реализацию в своих заголовках, она избыточно компилируется для каждого объекта, и любое изменение заголовка приведет к перекомпиляции всех зависимых объектов. Внешние функции в отдельных объектах могут быть изменены, этот отдельный объект перекомпилирован, затем он может быть связан с другими уже существующими объектами для формирования нового исполняемого файла. В крупных проектах это экономит МНОГО времени компиляции.

2 голосов
/ 08 декабря 2010

Есть несколько веских причин для разделения заголовка / реализации и отдельная подборка:
1. Это может быть требованием к работе - например, вы предоставляете двоичная библиотека + заголовок кому-либо, или ваши коллеги слишком консервативны принять что-нибудь еще.
2. Требуется разработка очень крупных проектов (скажем,> 10 млн. Источников), потому что восстановление всего приложения после каждой модификации станет болезненным. (Но все равно должно быть нормально скомпилировать что-то вроде jpeglib или zlib как единый модуль)
3. Существует мнение, что проще использовать заголовочные файлы для справки, чтобы функции и тому подобное. (Но обычно лучше написать правильную документацию; в отличие от заголовков, ошибки в документации с меньшей вероятностью влияют на вашу программу)

Кроме того, есть гораздо больше причин, чтобы не использовать его больше:
1. Вы хотели бы избежать сохранения дубликата кода.
2. Методы класса не нуждаются в предварительных объявлениях
3. В любом случае шаблоны могут быть объявлены только в заголовках
4. Случаи, когда вам не нужно вставлять функции, на самом деле довольно редки, т.е. вызывая большие функции несколько раз в узком цикле, но вы Атрибуты noinline и PGO для этого. В противном случае встраивание улучшает скорость. А что касается раздувания кода, большинство библиотек в любом случае уже огромны.
5. В целом, программы, скомпилированные как единый источник, быстрее и меньше, потому что компилятор может сделать лучшую работу.
6. Без заголовков источник часто будет примерно вдвое меньше, и компилятор сможет правильно проверить синтаксис, поэтому вы не сможете случайно связать внешний прототип функции "C" cdecl с переменной в качестве реализации. Кроме того, в целом, это было бы более переносимым, потому что разные линкеры имеют разные идеи о сопоставлении имен.
7. Это странно, но динамическое распределение часто используется только из-за стиль заголовка - зависимости могут быть разрешены автоматически путем определения всех детали внутри одного класса, но люди предпочитают вместо этого использовать указатели на частичные объявления классов (а затем охотятся за утечками памяти).

Теперь несколько бонусных баллов за отдельные объектные модули:
4. Статистика PGO в gcc генерируется для каждого объектного модуля, что, по-видимому, является единственным способом «сравнить» несколько различных режимов работы с одним исполняемым файлом.
5. Для оптимизации скорости можно компилировать разные модули с разными опциями компилятора. Для этого есть также некоторые расширения компилятора, но они не очень надежны.
6. Иногда компилятор может сделать что-то странное с другой частью кода, когда вы что-то модифицируете, но обычно он не может распространяться за пределы объектного модуля.

1 голос
/ 08 декабря 2010

Да, методы, помещенные в заголовок, являются встроенными, поэтому они, как правило, быстрее (особенно короткие).

Основной недостаток заключается в том, что каждая модификация в заголовке будет вызывать перекомпиляцию каждого файла, который его включает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...