Подтвердить что ты не робот

Почему бы не включить все стандартные заголовки?

Я читаю Herb Sutter More Exceptional C++ и элемент 37 в объявлениях вперед говорит:

Никогда #include заголовок, когда будет доступно объявление вперед. Предпочитайте #include только <iosfwd>, когда полное определение a поток не требуется.

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

Я прекрасно понимаю, почему это должно относиться к заголовкам проекта, но я не совсем понимаю, почему плохо включать ненужные заголовки standard.

Например, я делаю что-то вроде этого:

//standard_library.h

#ifndef STANDARD_LIBRARY
#define STANDARD_LIBRARY

#include <iostream>
#include <chrono>
#include <thread>
...
// Everything I need in the project
#endif

и включать этот единственный заголовок везде, где мне нужно что-то от std

Проблемы, которые я могу себе представить:

  • Загрязнение пространства имен с помощью функций библиотеки C, которые не должны находиться в пространстве имен std.
  • Более медленное время компиляции

Но у меня не было серьезных проблем с 1. sofar. Почти все в пространстве имен std. Кроме того, я не совсем понимаю, почему 2. обязательно является серьезной проблемой. Стандартные заголовки редко меняются. Кроме того, насколько я знаю, компилятор может прекомпилировать их. Когда дело доходит до шаблонов, они создаются (скомпилированы) только тогда, когда они мне нужны.

Есть также преимущества:

  • Меньшая настройка
  • Меньше чтения
  • Меньше выяснить, какие заголовки мне нужны и в каком заголовке есть определенная функция

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

4b9b3361

Ответ 1

Кроме

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

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

На практике, однако, я испытал (по крайней мере, с MFC/ATL) некоторые ошибки, которые могут быть решены путем определения правильного порядка включений. С другой стороны, в один прекрасный день вы хотите решить проблему, которая заставляет вас путешествовать по включенным заголовкам - теперь представьте себе, что вы смотрите на тонны заголовочных файлов, которые фактически не имеют никакого отношения к вашему кодовому файлу.

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

Ответ 2

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

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

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

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

Ответ 3

О! Я знаю хороший.

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

Он отлично работает на Linux и других POSIX-системах, но, как я пытался принять его в своем проекте, я остановился на этом: Как подавить #define локально?

И библиотека, и winbase.h(включенные через самый стандартный файл windows.h) имеют объект CreateFile. И, как и в winbase, это просто макросы, компилятор не видит никаких проблем, если вы на самом деле не пытаетесь использовать CreateFile в своем коде.

Итак, да, сохранение чистоты пространства имен может быть хорошей идеей.

Ответ 4

В принципе нет ничего против.

Единственное, что произойдет, это то, что время компиляции увеличится, если, конечно, вы не создадите предварительно скомпилированный заголовок этого standard_library.h, и в этом случае воздействие будет минимальным.

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

Ответ 5

Этот совет составляет около 10 лет. И несколько датируется к настоящему времени. Скорость компьютеров увеличивается в сотни раз, хранилище от G до T, безумный объем памяти сидит без дела.

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

Элемент травы более общий, чем ваш вопрос. С++ (несколько к сожалению) использует модель файла (единица перевода) для компиляции, а не репозиторий/базу данных исходного кода. (Те, кто пробовал Visual Age С++ IBM, знают, как это произошло.;) Следствием этого является то, что вы собрали много вещей. Включить файлы - это не один лайнер.

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

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

Говорите, что ваш заголовок использует указатели 10 разных классов. И вы включаете все 10 заголовков, определяющих их, а не просто префикс использования с помощью класса. Это означает, что любой клиент, который может использовать только несколько, действительно получает все десять втянутых в зависимость. Неэкономично. В проекте, который я работал несколько лет назад, использовался gtk ++. Файлы .cpp имели всего несколько сотен строк, но выход препроцессора составлял 800 тыс. Или более миллионов строк. Без шуток. Хотя вы платите за небольшую избыточность: сегодня может быть класс, но быть чем-то другим (например, typedef для шаблона). Идея _fwd.h смягчает это, но это действительно просто централизует избыточность. На практике мы добиваемся некоторого баланса в компромиссах.

Но все эти идеи не относятся к вещам, которые "стабильны" и повсеместно используются. В проектах, которые ставят std:: для тяжелого и естественного использования, вы можете видеть, и многие другие заголовки включены в каждый отдельный источник. Потому что они используются. И если какой-то рефакторинг удалит последний вектор сегодня, скорее всего, он вернется завтра.

Здесь выбор действительно только на том, "where" происходит включение, а экономика работает наоборот. Настройка "общего" заголовка, который используется всеми, удаляет много шума из других файлов. Особенно, если система поддерживает этот случай. В VS у вас есть

  • предварительно скомпилированные заголовки, которые позволяют однократно компилировать общий материал и совместно использовать результат с другими ТУ
  • принудительное включение, что позволяет использовать общий заголовок, указанный в проекте, а не в исходных файлах
  • которые вы включаете в файл проекта, вместо того, чтобы использовать эти параметры вручную

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

Те, кто говорит, что вы перетаскиваете много имен, которые могут вызвать конфликты, являются правильными - но в то же время они неправы для практики, поскольку в конечном итоге кто-то будет включать этот дополнительный заголовок, и если конфликт существует, он свергнет лодку. Если в проекте запрещено использование std:: stuff, я говорю, что просто неправильная практика повторного использования его общих имен для разных целей. Кто-то хочет проверить код со своей собственной строкой класса и утверждает, что он обязательно отличается от std::string префиксом, я называю "над моим мертвым телом". Хотя для редких имен это не имеет большого значения, чтобы разобраться в аварии.

И каковы хорошие изменения баланса по проектам и даже внутри проектов с течением времени.

Ответ 6

Более медленное время компиляции

Это основная причина.

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

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

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

Ответ 7

Когда изменяется заголовок, изменяются программы. Измененные программы должны быть протестированы. Быть избирательным минимизировать воздействие и, следовательно, тестирование.