#c #include
#c #включить
Вопрос:
Рассмотрим:
foo.h:
#include <string>
#include <vector>
class foo
{
std::string a;
std::vector<int> b;
};
bar.h:
#include <string>
#include <vector>
#include "foo.h" // <string> and <vector> included again
class bar
{
std::string c;
std::vector<bool> d;
};
Вопрос: требуются ли #include
s в foo.h? Я знаю, что защита заголовка предотвратит несколько #include
s, но можно ли опустить #include
s в foo.h?
РЕДАКТИРОВАТЬ: Извините, на самом деле я хотел спросить, можно ли опустить #include
s в bar.h, поскольку он уже включен в foo.h
Комментарии:
1.
foo.h
Нужны ли включения? Если это так, то сохраните их. Если нет, то их там не должно быть.2. После редактирования вы не должны полагаться на заголовок, включающий другие заголовки.
Ответ №1:
Полагаться на косвенные включения проблематично в любой нетривиальной базе кода: предполагая, что вы не включаете <string>
и <vector>
from bar.h
. Теперь, когда foo
изменения, например, для хранения последовательности символов по-другому и отбрасывает включение <string>
, ваш заголовок bar.h
разрывается. Таким образом, вы создаете ненужную зависимость.
В небольшом проекте эти сбои не имеют значения. В больших кодовых базах они могут легко стать катастрофическими. Также стоит отметить, что изменения в представлениях классов действительно работают!
Ответ №2:
Все стандартные заголовки имеют защиту включения, поэтому, с точки зрения компилятора, не имеет значения, включаете ли вы их в оба файла или только в один.
Но это имеет значение с точки зрения ясности кода: кто-то, читающий ваш файл, будет явно знать, что вам действительно нужно string
, и vector
для правильной работы этого заголовка. Это также упрощает вашу жизнь. Представьте, что a.h
использует b.h
, который использует c.h
, …, который использует z.h
, который использует <string>
. Не могли бы вы #include <string>
a.h
также опустить in? Анализ такого кода был бы беспорядочным.
Также обратите внимание, что если вы опустите includes bar.h
, вы можете столкнуться с проблемами в будущем, если вы включите bar.h
, но не включите <string>
или <vector>
где-то раньше. Поиск отсутствующих включений будет ужасным.
TL;DR всегда включают все необходимые заголовки. Недостатком является немного более длительное время компиляции, но есть намного больше преимуществ.
Комментарии:
1. Ясность — не единственная причина. Без включения
foo.h
будет нарушен, если не будет включен определенным образом в другом месте.2. @Spook Спасибо за объяснение.
Ответ №3:
Как и хуанчопанза, упомянутый в его комментарии, вы должны проверить на своем первом шаге, действительно ли inludes нужны в foo.h
and bar.h
. Если это так, вы должны сохранить их. Одна из причин заключается в том, что если вы (или кто-то, кроме вас) изменяете include bar.h
, вы всегда должны знать об этом и соответствующим образом изменять свой код. Поскольку общие заголовки защищены, вы можете просто избежать этого, записав их в каждый файл, где они необходимы. Кроме того, ваш код намного понятнее. Если кто-то попытается проникнуть в ваш код, он не увидит, какие заголовки нужны. Он должен был смотреть на каждое отдельное включение.