Повторное #включение в заголовки

#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 , вы всегда должны знать об этом и соответствующим образом изменять свой код. Поскольку общие заголовки защищены, вы можете просто избежать этого, записав их в каждый файл, где они необходимы. Кроме того, ваш код намного понятнее. Если кто-то попытается проникнуть в ваш код, он не увидит, какие заголовки нужны. Он должен был смотреть на каждое отдельное включение.