# включить в .h или .c / .cpp?

118

Когда я кодирую на C или C ++, где мне взять #include's?

callback.h:

#ifndef _CALLBACK_H_
#define _CALLBACK_H_

#include <sndfile.h>
#include "main.h"

void on_button_apply_clicked(GtkButton* button, struct user_data_s* data);
void on_button_cancel_clicked(GtkButton* button, struct user_data_s* data);

#endif

callback.c:

#include <stdlib.h>
#include <math.h>

#include "config.h"

#include "callback.h"
#include "play.h"

void on_button_apply_clicked(GtkButton* button, struct user_data_s* data) {
  gint page;
  page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->notebook));

  ...

Должны ли все включения быть в .h или .c / .cpp, или в обоих, как я сделал здесь?

Луиза
источник
2
Позвольте мне перевернуть это и спросить: что ваши критерии для принятия решения поставить sndfile.h и main.h в callback.h?
Оуэн С.

Ответы:

161

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

Брендан Лонг
источник
6
Верно, но разве это не #ifndef _CALLBACK_H_мешает компилятору обрабатывать его более одного раза?
hytromo
11
@ user9379 Это предотвратит его включение более одного раза в файл .c или .cpp. Каждый файл .c или .cpp обычно создается индивидуально, что означает, что .h будет повторно проанализирован для каждого компилируемого файла .c или .cpp.
Брендан Лонг
2
Я думаю, что основная причина, по которой нужно вкладывать как можно меньше в .hобъект, - это избежать в некоторых случаях ошибки из-за цикла включения. Пример: два класса нуждаются друг в друге для их реализации, но не для их объявлений. Включение обоих включений в .cpps позволит избежать ошибки.
Codoscope
1
@ Qu'est-cet'yont По этой причине вы не можете помещать определенные вещи в файлы .h. Этот ответ о том, почему вы должны ставить еще меньше.
Брендан Лонг,
@BrendanLong Я понимаю, хотя, в моем понимании, включение одного и того же заголовка несколько раз не имеет значения, если вы поместите внутри правильный макрос, чтобы включить контент только один раз. Поэтому я думаю, что ставить еще меньше - это уменьшить вероятность получить ошибку в будущем при модификации кода.
Codoscope
55

Единственный раз, когда вы должны включать заголовок в другой файл .h, - это если вам нужно получить доступ к определению типа в этом заголовке; например:

#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <stdio.h>

void doStuffWith(FILE *f); // need the definition of FILE from stdio.h

#endif

Если заголовок A зависит от заголовка B, как в примере выше, тогда заголовок A должен включать заголовок B напрямую. Как НЕ пытаться заказать ваш включает в .c файле для удовлетворения зависимостей (то есть, в том числе заголовка B до заголовка A); это большая куча изжоги, ожидающей своего часа. Я серьезно. Я был в этом фильме несколько раз, и он всегда заканчивался огнем Токио.

Да, это может привести к тому, что файлы будут включены несколько раз, но если у них есть надлежащие средства включения, настроенные для защиты от множественных ошибок объявления / определения, то несколько дополнительных секунд времени сборки не стоит беспокоиться. Попытка управлять зависимостями вручную - заноза в заднице.

Конечно, не следует включать файлы, в которых нет необходимости .

Джон Боде
источник
10

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

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

Parappa
источник
1
Оо. Интересна вещь форвардных деклараций.
Брендан Лонг
Параппа, форвардные объявления очень полезны в сценариях циклических ссылок. Но будут ли они хорошей практикой в ​​других сценариях? (Я новичок в C ++, поэтому честно спрашиваю)
Dzyann
5

Если да #include <callback.h>, я не хочу, чтобы у меня было #includeмного других файлов заголовков для компиляции моего кода. В callback.hнего следует включить все необходимое для компиляции. Но не более того.

Подумайте, будет ли достаточно использования форвардных объявлений в вашем файле заголовка (например, class GtkButton;), что позволит вам уменьшить количество #includeдиректив в заголовке (и, в свою очередь, время и сложность компиляции).

Johnsyweb
источник
Я не согласен. Включение всего мира в H-файлы увеличивает цепочку зависимостей и, следовательно, время компиляции.
Джон Диблинг 09
В моем ответе не рекомендуется включать весь мир в заголовочный файл, я предложил включить ровно столько, чтобы пользователю API не приходилось тратить время на поиск зависимостей.
Johnsyweb