Что должно войти в .h файл? - PullRequest
85 голосов
/ 22 декабря 2009

Когда вы делите свой код на несколько файлов, что именно должно входить в файл .h, а что - в файл .cpp?

Ответы [ 12 ]

100 голосов
/ 22 декабря 2009

Заголовочные файлы (.h) предназначены для предоставления информации, которая потребуется в нескольких файлах. Такие вещи, как объявления классов, прототипы функций и перечисления, обычно помещаются в заголовочные файлы. Одним словом, «определения».

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

Самый простой вопрос, который нужно задать себе, чтобы определить, что принадлежит, где находится: «Если я изменю это, придется ли мне менять код в других файлах, чтобы все снова компилировалось?» Если ответ «да», он, вероятно, принадлежит заголовочному файлу; если ответ «нет», он, вероятно, принадлежит к файлу кода.

49 голосов
/ 23 декабря 2009

На самом деле в C ++ это несколько сложнее, чем в организации заголовка C / источника.

Что видит компилятор?

Компилятор видит один большой исходный файл (.cpp) с правильно включенными заголовками. Исходный файл - это модуль компиляции, который будет скомпилирован в объектный файл.

Итак, зачем нужны заголовки?

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

В этом случае есть две копии одной и той же информации. Что является злом ...

Решение - поделиться некоторыми деталями. Хотя реализация должна оставаться в Source, объявление общих символов, таких как функции, или определение структур, классов, перечислений и т. Д. Может потребоваться для совместного использования.

Заголовки используются для размещения этих общих сведений.

Переместить в заголовок объявления о том, что необходимо разделить между несколькими источниками

Ничего больше?

В C ++ есть некоторые другие вещи, которые можно поместить в заголовок, потому что они тоже должны быть общими:

  • встроенный код
  • Шаблоны
  • константы (обычно те, которые вы хотите использовать внутри коммутаторов ...)

Переместить в заголовок ВСЕ, что необходимо передать, включая общие реализации

Значит ли это, что внутри заголовков могут быть источники?

Да. Фактически, есть много разных вещей, которые могут быть внутри «заголовка» (то есть разделены между источниками).

  • Форвардные декларации
  • объявления / определения функций / структур / классов / шаблонов
  • реализация встроенного и шаблонного кода

Это становится сложным, а в некоторых случаях (циклические зависимости между символами) невозможно сохранить его в одном заголовке.

Заголовки можно разбить на три части

Это означает, что в крайнем случае вы можете иметь:

  • заголовок предварительной декларации
  • заголовок объявления / определения
  • заголовок реализации
  • источник реализации

Давайте представим, что у нас есть шаблонный MyObject. Мы могли бы иметь:

// - - - - MyObject_forward.hpp - - - - 
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;

.

// - - - - MyObject_declaration.hpp - - - - 
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>

template<typename T>
class MyObject
{
   public :
      MyObject() ;
   // Etc.
} ;

void doSomething() ;

.

// - - - - MyObject_implementation.hpp - - - - 
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>

template<typename T>
MyObject<T>::MyObject()
{
   doSomething() ;
}

// etc.

.

// - - - - MyObject_source.cpp - - - - 
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>

void doSomething()
{
   // etc.
} ;

// etc.

Вау!

В «реальной жизни» это обычно менее сложно. Большая часть кода будет иметь только простую организацию заголовка / исходного кода с некоторым встроенным кодом в исходном коде.

Но в других случаях (объекты-шаблоны, зная друг друга), мне приходилось иметь для каждого объекта отдельные объявления и заголовки реализации с пустым источником, включая эти заголовки, просто чтобы помочь мне увидеть некоторые ошибки компиляции.

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

Заключение

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

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

^ _ ^

15 голосов
/ 22 декабря 2009

в дополнение ко всем остальным ответам, я скажу вам, что вы НЕ помещаете в заголовочный файл:
Объявление using (наиболее распространенным является using namespace std;) не должно появляться в заголовочном файле, поскольку оно загрязняет пространство имен исходного файла, в которое оно включено.

5 голосов
/ 22 декабря 2009

То, что компилируется в ничто (нулевой двоичный код) переходит в заголовочный файл.

Переменные не компилируются в ничто, а объявления типов - (потому что они только описывают, как ведут себя переменные).

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

шаблоны - это не код, а всего лишь рецепт создания кода. поэтому они также идут в h файлах.

3 голосов
/ 22 декабря 2009

Как правило, вы помещаете объявления в заголовочный файл и определения в файл реализации (.cpp). Исключением из этого являются шаблоны, где определение также должно идти в заголовке.

Этот вопрос и подобные ему часто задавались в SO - см. Почему в C ++ имеются заголовочные файлы и файлы .cpp? и Заголовочные файлы C ++, например, Разделение кода .

1 голос
/ 22 декабря 2009

В основном заголовочный файл содержит скелет класса или объявление (не часто меняется)

и файл cpp содержит реализацию класса (часто изменяется).

1 голос
/ 22 декабря 2009

Ваши объявления классов и функций, а также документация и определения встроенных функций / методов (хотя некоторые предпочитают помещать их в отдельные файлы .inl).

0 голосов
/ 22 декабря 2009
  • Заголовочные файлы - не должны меняться во время разработки слишком часто -> вы должны подумать и написать их сразу (в идеальном случае)
  • Исходные файлы - изменения во время реализации
0 голосов
/ 22 декабря 2009

Заголовок Что-то определяет , но ничего не говорит о реализации. (За исключением шаблонов в этом «метафоре».

С учетом вышесказанного вам необходимо разделить "определения" на подгруппы, в этом случае существует два типа определений.

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

Теперь я, конечно, говорю о первой подгруппе.

Заголовок предназначен для определения макета вашей структуры, чтобы помочь остальной части программного обеспечения использовать реализацию. Возможно, вы захотите рассматривать это как «абстракцию» вашей реализации, о чем нехорошо сказано, но, думаю, в этом случае она вполне подходит.

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

0 голосов
/ 22 декабря 2009

Заголовок (.h)

  • Макросы и включения, необходимые для интерфейсов (как можно меньше)
  • Объявление функций и классов
  • Документация по интерфейсу
  • Объявление встроенных функций / методов, если таковые имеются
  • вне глобальных переменных (если есть)

Body (.cpp)

  • Остальные макросы и включает
  • Включите заголовок модуля
  • Определение функций и методов
  • Глобальные переменные (если есть)

Как правило, вы помещаете «разделяемую» часть модуля в .h (ту часть, которую должны видеть другие модули) и «не разделяемую» часть в .cpp

PD: Да, я включил глобальные переменные. Я использовал их несколько раз, и важно не определять их в заголовках, иначе вы получите множество модулей, каждый из которых определяет свою собственную переменную.

РЕДАКТИРОВАТЬ: Изменено после комментария Дэвида

...