Зачем C ++ нужен отдельный заголовочный файл?

Мы никогда не понимали, почему C ++ нуждается в отдельном заголовочном файле с теми же функциями, что и в файле .cpp. Это делает создание классов и их рефакторинг очень сложным и добавляет ненужные файлы в проект. И тогда возникает проблема с необходимостью включения заголовочных файлов, но с явной проверкой, если они уже были включены.

C ++ был ратифицирован в 1998 году, так почему же он разработан таким образом? Какие преимущества имеет наличие отдельного заголовочного файла?

Контрольный вопрос:

Как компилятор находит файл .cpp с кодом в нем, когда я включаю только файл .h? Предполагается ли, что файл .cpp имеет то же имя, что и файл .h, или он просматривает все файлы в дереве каталогов?

 Peter Mortensen20 авг. 2009 г., 16:05
Точная копия:stackoverflow.com/questions/333889, Почти дубликат:stackoverflow.com/questions/752793
 Richard Corden20 авг. 2009 г., 14:51
Если вы хотите редактировать только один файл, оформите заказ lzz (www.lazycplusplus.com).

Ответы на вопрос(13)

Ну, вы можете прекрасно разработать C ++ без заголовочных файлов. Фактически некоторые библиотеки, которые интенсивно используют шаблоны, не используют парадигму заголовочных файлов / файлов кода (см. Boost). Но в C / C ++ нельзя использовать то, что не объявлено. Один из практических способов справиться с этим - использовать заголовочные файлы. Кроме того, вы получаете преимущество совместного использования интерфейса без совместного использования кода / реализации. И я думаю, что создатели C не предполагали этого: когда вы используете общие заголовочные файлы, вы должны использовать знаменитые:

#ifndef MY_HEADER_SWEET_GUARDIAN
#define MY_HEADER_SWEET_GUARDIAN

// [...]
// my header
// [...]

#endif // MY_HEADER_SWEET_GUARDIAN

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

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

Еще одно бремя для нас, бедных пользователей C ++ ...

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

Да, на данный момент (через 10 лет после первого стандарта C ++ и через 20 лет после того, как он начал серьезно расти в использовании), легко спросить, почемуу него есть правильная система модулей. Очевидно, что любой новый язык, разрабатываемый сегодня, не будет работать как C ++. Но это неТочка C ++.

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

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

Поэтому вместо того, чтобы ожидать, что C ++ превратится в C # (что было бы бессмысленно, поскольку у нас уже есть C #), почему бы просто не выбрать правильный инструмент для этой работы? Сам я стараюсь писать значительные куски новой функциональности на современном языке (я использую C #), и у меня есть большое количество существующих C ++, которые я храню в C ++, потому что не было бы никакой реальной ценности в переписывании этого все. В любом случае, они очень хорошо интегрируются, так чтов основном безболезненно.

 Peter Mortensen20 авг. 2009 г., 16:23
Как вы интегрируете C # и C ++? Через COM?
 Daniel Earwicker20 авг. 2009 г., 17:58
Есть три основных способаЛучший" зависит от вашего существующего кода. Я'мы использовали все три. Больше всего я использую COM, потому что мой существующий код уже был разработан вокруг него, поэтомуЭто практически без проблем, работает очень хорошо для меня. В некоторых странных местах я использую C ++ / CLI, который дает невероятно плавную интеграцию для любой ситуации, когда вы неУ вас уже есть COM-интерфейсы (и вы можете предпочесть использовать существующие COM-интерфейсы, даже если они у вас есть). Наконец, есть p / invoke, который в основном позволяет вам вызывать любую C-подобную функцию, предоставляемую из DLL, поэтому вы можете напрямую вызывать любой Win32 API из C #.

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

Например, следующее должно дать ошибку компилятора:

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

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

void SomeOtherFunction();

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

Это позволяет компилятору узнать: где-то в коде есть функция SomeOtherFunction, которая возвращает void и не принимает никаких параметров. Поэтому, если вам нужен код, который пытается вызвать SomeOtherFunction, не паникуйте, а вместо этого отправляйтесь на его поиск.

Теперь представьте, что у вас есть SomeFunction и SomeOtherFunction в двух разных файлах .c. Затем вы должны #include "SomeOther.c» в некоторых Теперь добавьте немногочастный" функционирует SomeOther.c. Поскольку C не знает приватных функций, эта функция также будет доступна в Some.c.

Вот где приходят файлы .h: они определяют все функции (и переменные), которые вы хотитеЭкспорт» из .c файла, который может быть доступен в других .c файлах. Таким образом, вы получаете что-то вроде публичного / частного объема. Кроме того, вы можете передать этот файл .h другим людям без необходимости делиться своим исходным кодом - файлы .h также работают с скомпилированными файлами .lib.

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

Это был С, хотя. C ++ представил классы и частные / публичные модификаторы, поэтому, хотя вы все еще можете спросить, нужны ли они, C ++ AFAIK по-прежнему требует объявления функций перед их использованием. Кроме того, многие разработчики C ++ являются или были разработчиками языка C и переняли свои концепции и привычки на C ++ - зачем менять то, что нет?Т сломан?

 Michael Stum20 авг. 2009 г., 15:28
Я предполагаю, что в 1972 году это могло быть довольно дорогой операцией для компилятора.
 MSalters20 авг. 2009 г., 16:10
Ага - компилирование на 16 биттеров с массой ленточных торей нетривиально.
 jalf20 авг. 2009 г., 15:40
Именно то, что сказал Майкл Стум. Когда это поведение было определено, единственное, что могло быть практически осуществлено, - это линейное сканирование через единицу перевода.
 Puddle11 июн. 2018 г., 14:05
это лучший ответ не только на этой странице, но и во всем Интернете. не могли бы вы подробнее рассказать о "Кроме того, вы можете передать этот файл .h другим людям без необходимости делиться своим исходным кодом - файлы .h также работают с скомпилированными файлами .lib. ".... Это прямо здесь, похоже, истинная причина .h файлы существуют. так как в Java, например, если вы запутываете имена методов jars, это неэто будет полезная библиотека для раздачи. так что вы могли бы еще запутать тела, но сохранить имена этих методов в скомпилированном источнике. что сделает декомпиляцию вашего источника легкой для людей.
 Puddle11 июн. 2018 г., 16:50
@MichaelStum, но почему тогда? зачем им это держать? язык о понимании цели того, что пишет программист. каждый может понять, как создавать заголовки на основе всех классов. Это'Это бессмысленная задача, если она буквально ничего не делает, но помогает компилировать c ++. мы'Мы пошли дальше и могли бы сделать это автоматизированным, если бы он больше ничего не делал. если это не служит никакой другой цели ...
 repzero29 мая 2016 г., 05:42
Я новичок в изучении c ++, но это объяснение кормления ложкой навсегда поразило мои мысли, вместо того, чтобы принимать терминологию "Вот как это делает C, так что C ++ наследует его »..... Можете ли вы посоветовать мне, когда использовать пользовательские файлы заголовков ??? .... Я предполагаю, что это может подойти для очень большого приложения, где должны поддерживаться отдельные файлы C ++, что вызывает необходимость в файлах заголовков
 repzero29 мая 2016 г., 05:49
@MichaelStum .... не будет ли более утомительным создание и поддержка заголовочных файлов (хотя бы) ?? ... например, если я внесу несколько изменений в типы переменных в файле cpp, у меня будет вставить / изменить объявления переменных в заголовочном файле .... хмм ....
 Marius20 авг. 2009 г., 15:16
Почему можнот компилятор прогоняет код и находит все определения функций? Кажется, что-то, что было бы довольно легко запрограммировать в компилятор.
 DevSolar20 авг. 2009 г., 15:27
если тыиметь источник, который вы часто не делаетеесть Скомпилированный C ++ - это по сути машинный код с достаточным количеством дополнительной информации для загрузки и связывания кода. Затем вы указываете CPU на точку входа и запускаете его. Это принципиально отличается от Java или C #, где код компилируется в промежуточный байт-код, содержащий метаданные своего содержимого.
 Michael Stum11 июн. 2018 г., 16:46
@ Лужа, я нене думаю, чтоистинная причина, потому что в 70-хКогда C был разработан, совместное использование исходного кода было нормой, а не исключением. Я верю этому'потому что случайный доступ к файлам не былЭто легко возможно - в то время использование магнитных лент было обычным делом, и поэтому язык можно было скомпилировать, только продвигаясь вперед по файлам, а не назад или перепрыгивая. Файлы .h кажутся отличным способом продвинуть объявления вперед, не внося еще большего беспорядка в конфликтующие реализации.

Ну, C ++ был ратифицирован в 1998 году, но он использовался гораздо дольше, и ратификация в первую очередь определяла текущее использование, а не навязывала структуру. И поскольку C ++ был основан на C, а C имеет заголовочные файлы, в C ++ они тоже есть.

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

Скажем, у меня есть foo.cpp, и я хочу использовать код из файлов bar.h / bar.cpp.

Я могу включитьbar.h» в foo.cpp, а затем запрограммируйте и скомпилируйте foo.cpp, даже если bar.cpp не 'не существует. Заголовочный файл действует как обещание компилятору, что классы / функции в bar.h будут существовать во время выполнения, и в нем есть все, что ему нужно уже знать.

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

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

Другое заключается в том, что если вы измените реализацию своего кода в файле * .cpp, но вообще не измените заголовок, вам нужно только скомпилировать файл * .cpp вместо всего, что его использует. Конечно, если вы поместите много реализации в заголовочный файл, это станет менее полезным.

Некоторые люди считают заголовочные файлы преимуществом:

Утверждается, что он включает / обеспечивает / разрешает / разделяет интерфейс и реализацию - но обычно это не так. Заголовочные файлы полны деталей реализации (например, переменные-члены класса должны быть указаны в заголовке, даже если онине являются частью общедоступного интерфейса), и функции могут и часто определяются как встроенныев объявление класса в заголовке, снова разрушающее это разделение.Иногда говорят, что это улучшает время компиляции, потому что каждая единица перевода может обрабатываться независимо. И все же C ++, вероятно, самый медленный из существующих языков, когда дело доходит до компиляции. Часть причины - много многократных включений того же самого заголовка. Большое количество заголовков включено несколькими модулями перевода, что требует их многократного анализа.

В конечном итоге, система заголовков является артефактом от 70 'с, когда C был разработан. Тогда у компьютеров было очень мало памяти, и хранение всего модуля в памяти просто не былот вариант. Компилятор должен был начать чтение файла сверху, а затем продолжить линейно через исходный код. Механизм заголовка позволяет это. Компилятор ненужно учитывать другие единицы перевода, просто нужно читать код сверху вниз.

И C ++ сохранил эту систему для обратной совместимости.

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

Однако одно из предложений для C ++ 0x заключалось в том, чтобы добавить правильную систему модулей, позволяющую компилировать код, подобный .NET или Java, в более крупные модули, причем все за один раз и без заголовков. Это предложение неЯ не верю в C ++ 0x, но я верю в этовсе еще в "мы'буду любить делать это позже категория. Возможно в TR2 или аналогичном.

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

Второе преимущество: это позволяет совместно использовать интерфейсы без разделения кода между различными подразделениями (разными разработчиками, командами, компаниями и т. Д.)

 Vlagged20 авг. 2009 г., 15:01
Вы намекаете на это, например, в C # 'Вы должны будете включить исходные файлы в другие исходные файлы " ? Потому что, очевидно, ты нет. Во-вторых, я думаю, чтослишком зависит от языка: вы не будете использовать файлы .h, например, Delphi
 jalf20 авг. 2009 г., 15:38
Так подожди, тысерьезно заявляем, что C ++Модель компиляции более эффективна, чем в C #? Система заголовков вызывает огромные накладные расходы, которых можно было бы избежать в нормальной системе сборки.
 Puddle11 июн. 2018 г., 14:16
Как знание имен позволяет использовать скомпилированный источник? как они связывают? слова еще в двоичном или что-то еще? Что делать, если вы изменили имя в заголовочном файле? будет ли он ссылаться на метод, который он представляет?
 erelender20 авг. 2009 г., 15:57
Как я уже сказал, я нене думайте об этом вопросе как о сравнении языков, просто о сравнении сценариев использования.
 gbjbaanb20 авг. 2009 г., 15:18
C # не делаетНе включайте исходные файлы в другие, но вы все равно должны ссылаться на модули - и это заставляет компилятор извлекать исходные файлы (или отражать в двоичный файл), чтобы проанализировать символы, которые использует ваш код.
 neuro20 авг. 2009 г., 15:14
хорошо, но я неНе думаю, что это особенность языка. Что-то более практичное, чтобы иметь дело с декларацией C до ее определения "проблема», Это как кто-то известное высказываниетот'это не ошибка, котораяособенность " :)
 Marius20 авг. 2009 г., 15:06
В любом случае вам придется перекомпилировать весь проект, так что же, на самом деле, учитывается первое преимущество?
 erelender20 авг. 2009 г., 15:17
@Marius: Да, это действительно важно. Связывание всего проекта отличается от компиляции исвязывание всего проекта. По мере увеличения количества файлов в проекте компиляция всех этих файлов становится действительно раздражающей. @Vlagged: Вы правы, но я неСравнивать С ++ с другим языком. Я сравнил использование только исходных файлов с использованием исходного кода &заголовочные файлы.
 Martin Beckett20 авг. 2009 г., 15:55
Да, если альтернатива простому заголовочному файлу - изобретать виртуальную машину, рефлексию, общую среду выполнения и т. Д.!
 Puddle11 июн. 2018 г., 14:10
Это'Прошло уже почти десять лет с тех пор, как вы ответили, но может ли кто-нибудь объяснить, как заголовочные файлы препятствуют обмену кодом? и я имею в виду, логика этого. в Java единственным средством защиты кода, о котором я знаю, является запутывание. как это работает в C ++ с заголовочными файлами?

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

Это'Это действительно проблема масштаба.

Решение Вопроса

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

Ответ в том, что C ++ не "необходимость" этот. Если вы пометите все как встроенное (что в любом случае является автоматическим для функций-членов, определенных в определении класса), разделение не требуется. Вы можете просто определить все в заголовочных файлах.

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

Чтобы улучшить время сборки.Для ссылки на код, не имея источника для определений.Чтобы не отмечать все "в соответствии".

Если ваш более общий вопрос "почему нетт С ++ идентичен Java? "тогда я должен спросить "почему ты пишешь C ++ вместо Java? " ;-п

А если серьезно, причина в том, что компилятор C ++ можетпросто достучаться до другой единицы перевода и выяснить, как использовать ее символы так, как это делает и делает javac. Заголовочный файл необходим, чтобы объявить компилятору, что он может ожидать быть доступным во время ссылки.

Так#include прямая текстовая замена. Если вы определите все в заголовочных файлах, препроцессор в итоге создаст огромную копию и вставку каждого исходного файла в вашем проекте и загрузит его в компилятор. Тот факт, что стандарт C ++ был ратифицирован в 1998 году, не имеет к этому никакого отношения.тот факт, что среда компиляции для C ++ так тесно основана на среде C.

Преобразование моих комментариев, чтобы ответить на ваш дополнительный вопрос:

Как компилятор находит файл .cpp с кодом в нем

Это нет, по крайней мере, не во время компиляции кода, который использовал файл заголовка. Функции, которые выре связываюсь против недаже не нужно писать, не говоря уже о компиляторе, зная, что.cpp подать ониЯ буду внутри. Все, что нужно знать вызывающему коду во время компиляции, выражается в объявлении функции. Во время ссылки вы предоставите список.o файлы, или статические, или динамические библиотеки, а действующий заголовок - это обещание, что определения функций где-то там будут.

 Błażej Michalik10 апр. 2016 г., 14:37
@AndresCanella Нет, это не так. Это делает чтение и ведение не-вашего-собственного кода кошмаром. Чтобы полностью понять, что что-то делает в коде, вам нужно перейти через 2n файлов вместо n файлов. Это просто неОбозначение Big-Oh, 2n имеет большое значение по сравнению с просто n.
 Dmitry16 авг. 2016 г., 20:01
Во-вторых, это ложь, что заголовки помогают. проверьте источник Minix, например, этотак трудно проследить, где оно начинается, где передается управление, где вещи объявляются / определяются ... если бы оно было построено с помощью отдельных динамических модулей, это было бы легко понять, если понять одну вещь, а затем перейти к модулю зависимости. вместо этого вам нужно следовать заголовкам, и это делает чтение любого кода, написанного таким образом, чертовски. в отличие от этого, nodejs проясняет, откуда что происходит, без каких-либо ifdef, и вы можете легко определить, откуда это пришло.
 Steve Jessop19 мар. 2015 г., 10:02
Я намеревался включить это в (2), "ссылка на код без определения ", Хорошо, так что у вас все еще может быть доступ к git-репозиторию с определениями в нем, но дело в том, что вы можете скомпилировать свой код отдельно от реализаций его зависимостей и затем ссылаться. Если буквально все, что вам нужно, это разделить интерфейс / реализацию на разные файлы, не заботясь о сборке по отдельности, то вы можете сделать это, просто включив в foo_interface.h файл foo_implementation.h.
 darth_coder19 мар. 2015 г., 08:09
Проще говоря, я думаю о полезности разделения файлов заголовка и cpp - это разделение интерфейса и реализации, что действительно помогает для средних и больших проектов.
 Andres Canella12 июл. 2012 г., 18:19
Добавить в "Причины, которые вы могли бы хотеть отделить: & Я считаю, что самая важная функция заголовочных файлов: отделить проект структуры кода от реализации, потому что: A. Когда вы попадаете в действительно сложные структуры, которые включают много объектов, гораздо проще просканировать заголовочные файлы и вспомнить, как они работают вместе, дополнены вашими заголовками комментариев. Б. Один человек не позаботился о том, чтобы определить всю структуру объекта, а кто-то другой позаботился о реализации, чтобы все было организовано. В целом, я думаю, что это делает сложный код более читабельным.

C ++ был ратифицирован в 1998 году, так почему же он разработан таким образом? Какие преимущества имеет наличие отдельного заголовочного файла?

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

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

Более новые скомпилированные языки (такие как Java, C #) не используют предварительные объявления; идентификаторы распознаются автоматически из исходных файлов и считываются непосредственно из символов динамической библиотеки. Это означает, что заголовочные файлы не нужны.

 MSalters20 авг. 2009 г., 16:01
+1 Ударяет ноготь по голове. Это действительно нене требует подробного объяснения.
 Alexander Taylor01 авг. 2015 г., 02:11
Это неЯ не могу понять, почему C ++ должен использовать предварительные объявления и почему он может это сделать.t распознавать идентификаторы из исходных файлов и читать непосредственно из символов динамической библиотеки, и почему C ++ сделал это именно потому, что C сделал это именно так: p
 Donald Byrd02 авг. 2015 г., 04:13
И вы лучший программист, сделав это @AlexanderTaylor :)

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

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

Проверьтеэтот предыдущий пост для дальнейшего обсуждения ...

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

Чтобы компенсировать это ограничение, C и C ++ допускают объявления, и эти объявления могут быть включены в модули, которые используют их с помощью препроцессора.s #include директива.

Такие языки, как Java или C #, с другой стороны, содержат информацию, необходимую для связывания в компиляторе.s вывод (файл класса или сборка). Следовательно, больше нет необходимости поддерживать автономные объявления, включаемые клиентами модуля.

Причина, по которой информация о привязке не включается в выходные данные компилятора, проста: она не требуется во время выполнения (любая проверка типов выполняется во время компиляции). Это просто пустое место. Помните, что C / C ++ пришел из того времени, когда размер исполняемого файла или библиотеки действительно имел значение.

 smwikipedia13 сент. 2010 г., 18:18
Я с тобой согласен. У меня есть похожая идея здесь:stackoverflow.com/questions/3702132/...

Ваш ответ на вопрос