Я никогда не понимал, почему C ++ нуждается в отдельном заголовочном файле с теми же функциями, что и в файле .cpp. Это делает создание классов и их рефакторинг очень сложным, а также добавляет ненужные файлы в проект. И тогда возникает проблема с необходимостью включения заголовочных файлов, но с явной проверкой, если они уже были включены.
C ++ был ратифицирован в 1998 году, так почему же он разработан таким образом? Какие преимущества имеет наличие отдельного заголовочного файла?
Контрольный вопрос:
Как компилятор находит файл .cpp с кодом в нем, когда я включаю только файл .h? Предполагается ли, что файл .cpp имеет то же имя, что и файл .h, или он просматривает все файлы в дереве каталогов?
c++
language-design
Marius
источник
источник
Ответы:
Похоже, вы спрашиваете об отделении определений от объявлений, хотя существуют и другие способы использования заголовочных файлов.
Ответ в том, что C ++ не «нуждается» в этом. Если вы пометите все как встроенное (что в любом случае является автоматическим для функций-членов, определенных в определении класса), разделение не требуется. Вы можете просто определить все в заголовочных файлах.
Причины, которые вы могли бы хотеть отделить:
Если ваш более общий вопрос «почему C ++ не идентичен Java?», То я должен спросить: «Почему вы пишете C ++ вместо Java?» ;-п
Более серьезно, однако, причина в том, что компилятор C ++ не может просто добраться до другого модуля перевода и выяснить, как использовать его символы так, как это делает и делает javac. Заголовочный файл необходим, чтобы объявить компилятору, что он может ожидать быть доступным во время ссылки.
Так
#include
что прямая текстовая замена. Если вы определите все в заголовочных файлах, препроцессор в итоге создаст огромную копию и вставку каждого исходного файла в вашем проекте и загрузит его в компилятор. Тот факт, что стандарт C ++ был ратифицирован в 1998 году, не имеет к этому никакого отношения, это тот факт, что среда компиляции для C ++ так тесно основана на среде C.Преобразование моих комментариев, чтобы ответить на ваш дополнительный вопрос:
Это не так, по крайней мере, во время компиляции кода, который использовал файл заголовка. Функции, с которыми вы ссылаетесь, даже не нужно писать, не говоря уже о том, что компилятор знает, в каком
.cpp
файле они будут находиться. Все, что нужно знать вызывающему коду во время компиляции, выражается в объявлении функции. Во время ссылки вы предоставите список.o
файлов, или статических, или динамических библиотек, а действующий заголовок обещает, что определения функций где-то там будут.источник
C ++ делает это таким образом, потому что C сделал это таким образом, поэтому реальный вопрос в том, почему C сделал это таким образом? Википедия немного говорит об этом.
источник
Некоторые люди считают заголовочные файлы преимуществом:
В конечном счете, система заголовков является артефактом 70-х годов, когда был разработан С. Тогда у компьютеров было очень мало памяти, и хранить весь модуль в памяти было просто невозможно. Компилятор должен был начать чтение файла сверху, а затем продолжить линейно через исходный код. Механизм заголовка позволяет это. Компилятору не нужно учитывать другие единицы перевода, он просто должен читать код сверху вниз.
И C ++ сохранил эту систему для обратной совместимости.
Сегодня это не имеет смысла. Это неэффективно, подвержено ошибкам и слишком сложно. Есть гораздо лучшие способы разделить интерфейс и реализацию, если это и было целью.
Однако одно из предложений для C ++ 0x заключалось в том, чтобы добавить правильную систему модулей, позволяющую компилировать код, подобный .NET или Java, в более крупные модули, все за один раз и без заголовков. Это предложение не привело к сокращению C ++ 0x, но я считаю, что оно все еще находится в категории «мы бы хотели сделать это позже». Возможно в TR2 или аналогичном.
источник
Насколько я понимаю (ограниченно - я не являюсь разработчиком на C), это коренится в C. Помните, что C не знает, что такое классы или пространства имен, это всего лишь одна длинная программа. Кроме того, функции должны быть объявлены перед их использованием.
Например, следующее должно дать ошибку компилятора:
Ошибка должна заключаться в том, что «SomeOtherFunction не объявлена», потому что вы вызываете ее до ее объявления. Один из способов исправить это - переместить SomeOtherFunction над SomeFunction. Другой подход - сначала объявить сигнатуру функции:
Это позволяет компилятору узнать: где-то в коде есть функция SomeOtherFunction, которая возвращает void и не принимает никаких параметров. Поэтому, если вам нужен код, который пытается вызвать SomeOtherFunction, не паникуйте, а вместо этого отправляйтесь на его поиск.
Теперь представьте, что у вас есть SomeFunction и SomeOtherFunction в двух разных файлах .c. Затем вы должны #include "SomeOther.c" в Some.c. Теперь добавьте некоторые «частные» функции в SomeOther.c. Поскольку C не знает частных функций, эта функция будет доступна и в Some.c.
Вот где приходят файлы .h: они определяют все функции (и переменные), которые вы хотите «экспортировать» из файла .c, к которому можно получить доступ в других файлах .c. Таким образом, вы получаете что-то вроде публичного / частного объема. Кроме того, вы можете передать этот файл .h другим людям без необходимости делиться своим исходным кодом - файлы .h также работают с скомпилированными файлами .lib.
Таким образом, главная причина на самом деле для удобства, для защиты исходного кода и для небольшого разделения между частями вашего приложения.
Это был С, хотя. C ++ представил классы и частные / публичные модификаторы, поэтому, хотя вы все еще можете спросить, нужны ли они, C ++ AFAIK все еще требует объявления функций перед их использованием. Кроме того, многие разработчики C ++ являются или были разработчиками C и переняли свои концепции и привычки на C ++ - зачем менять то, что не нарушено?
источник
Первое преимущество: если у вас нет заголовочных файлов, вам придется включить исходные файлы в другие исходные файлы. Это приведет к повторной компиляции включаемых файлов при изменении включенного файла.
Второе преимущество: это позволяет совместно использовать интерфейсы без разделения кода между разными подразделениями (разными разработчиками, командами, компаниями и т. Д.)
источник
Потребность в заголовочных файлах проистекает из ограничений, которые имеет компилятор для знания информации о типе для функций и / или переменных в других модулях. Скомпилированная программа или библиотека не содержат информацию о типе, требуемую компилятором для привязки к любым объектам, определенным в других модулях компиляции.
Чтобы компенсировать это ограничение, C и C ++ допускают объявления, и эти объявления могут быть включены в модули, которые используют их с помощью директивы препроцессора #include.
Такие языки, как Java или C #, с другой стороны, включают информацию, необходимую для привязки, в выходных данных компилятора (файл класса или сборка). Следовательно, больше нет необходимости поддерживать автономные объявления, которые будут включены клиентами модуля.
Причина, по которой информация о привязке не включается в выходные данные компилятора, проста: она не требуется во время выполнения (любая проверка типов выполняется во время компиляции). Это просто пустое место. Помните, что C / C ++ произошел в то время, когда размер исполняемого файла или библиотеки имел большое значение.
источник
C ++ был разработан для добавления современных функций языка программирования в инфраструктуру C, без излишнего изменения чего-либо в C, что не относится конкретно к самому языку.
Да, в этот момент (через 10 лет после первого стандарта C ++ и через 20 лет после того, как он начал серьезно расти в использовании), легко спросить, почему у него нет надлежащей системы модулей. Очевидно, что любой новый язык, разрабатываемый сегодня, не будет работать как C ++. Но дело не в C ++.
Смысл C ++ в том, чтобы быть эволюционным, плавным продолжением существующей практики, только добавляя новые возможности без (слишком часто) ломая вещи, которые адекватно работают для его сообщества пользователей.
Это означает, что это делает некоторые вещи сложнее (особенно для людей, начинающих новый проект), а некоторые вещи легче (особенно для тех, кто поддерживает существующий код), чем другие языки.
Поэтому вместо того, чтобы ожидать, что C ++ превратится в C # (что было бы бессмысленно, поскольку у нас уже есть C #), почему бы просто не выбрать правильный инструмент для работы? Сам я стараюсь писать значительные куски новой функциональности на современном языке (я использую C #), и у меня есть большое количество существующих C ++, которые я храню в C ++, потому что не было бы никакой реальной ценности в переписывании этого все. В любом случае, они очень хорошо интегрируются, поэтому в основном безболезненно.
источник
Ну, C ++ был ратифицирован в 1998 году, но он использовался гораздо дольше, и ратификация в первую очередь определяла текущее использование, а не навязывала структуру. И поскольку C ++ был основан на C, а C имеет заголовочные файлы, в C ++ они тоже есть.
Основная причина для заголовочных файлов состоит в том, чтобы включить отдельную компиляцию файлов и минимизировать зависимости.
Скажем, у меня есть foo.cpp, и я хочу использовать код из файлов bar.h / bar.cpp.
Я могу #include "bar.h" в foo.cpp, а затем запрограммировать и скомпилировать foo.cpp, даже если bar.cpp не существует. Заголовочный файл действует как обещание компилятору, что классы / функции в bar.h будут существовать во время выполнения, и в нем есть все, что ему нужно уже знать.
Конечно, если функции в bar.h не имеют тел, когда я пытаюсь связать свою программу, она не будет связываться, и я получу ошибку.
Побочным эффектом является то, что вы можете дать пользователям файл заголовка, не раскрывая свой исходный код.
Другое заключается в том, что если вы измените реализацию своего кода в файле * .cpp, но вообще не измените заголовок, вам нужно только скомпилировать файл * .cpp вместо всего, что его использует. Конечно, если вы поместите много реализации в заголовочный файл, это станет менее полезным.
источник
Ему не нужен отдельный заголовочный файл с теми же функциями, что и в main. Это нужно только в том случае, если вы разрабатываете приложение, используя несколько файлов кода, и если вы используете функцию, которая не была ранее объявлена.
Это действительно проблема масштаба.
источник
На самом деле, заголовочные файлы становятся очень полезными при первом рассмотрении программ. Проверка заголовочных файлов (с использованием только текстового редактора) дает вам обзор архитектуры программы, в отличие от других языков, где вам нужно использовать сложные инструменты для просмотра классов и их функции-члены.
источник
Я думаю , что реальные (историческая) причина заголовочных файлов была сделать , как проще для разработчиков компиляторов ... но тогда, файлы заголовков действительно дают преимущество.
Проверьте это предыдущее сообщение для большего количества обсуждений ...
источник
Ну, вы можете прекрасно разработать C ++ без заголовочных файлов. Фактически, некоторые библиотеки, которые интенсивно используют шаблоны, не используют парадигму заголовочных файлов / файлов кода (см. Boost). Но в C / C ++ нельзя использовать то, что не объявлено. Один из практических способов справиться с этим - использовать заголовочные файлы. Кроме того, вы получаете преимущество совместного использования интерфейса без совместного использования кода / реализации. И я думаю, что создатели C не предполагали этого: когда вы используете общие заголовочные файлы, вы должны использовать знаменитые:
на самом деле это не языковая функция, а практический способ справиться с множественным включением.
Итак, я думаю, что когда создавался C, проблемы с предварительным объявлением недооценивались, и теперь при использовании языка высокого уровня, такого как C ++, нам приходится иметь дело с такого рода вещами.
Еще одно бремя для нас, бедных пользователей C ++ ...
источник
Если вы хотите, чтобы компилятор обнаруживал символы, определенные в других файлах, автоматически, вы должны заставить программиста поместить эти файлы в предопределенные места (например, структура пакетов Java определяет структуру папок проекта). Я предпочитаю заголовочные файлы. Также вам понадобятся источники используемых вами библиотек или какой-то унифицированный способ размещения информации, необходимой компилятору, в двоичные файлы.
источник