Зачем размещать объявления и определения функций в отдельных файлах?
Автор вопроса: CommunityОтветов: 5
В уроках codeacademy по функциям вас учат использовать три файла, если вы собираетесь вызывать функции в своей программе:
файл int main(), который я нашел путем проб и ошибок, является неотъемлемой частью программной части программы на С++ (я думаю...) с расширением файла .cpp.
файл заголовка для функций DECLARING с расширением .hpp.
отдельный файл с функцией DEFINITIONS с расширением .cpp
Будет ли работать как объявление, так и определение функций в заголовочном файле отдельно и просто включение их выше int main()? Мне кажется, что наличие отдельных файлов для объявлений и определений может привести к путанице в более крупном проекте.
На работе я пишу программы длиной в 50+ тысяч строк. Представьте, сколько времени потребовалось бы на компиляцию, если бы все было в одном файле. Я бы абсолютно ничего не сделал. drescherjm 06.06.2020
На самом деле все наоборот. Даже в умеренно большом проекте могут быть десятки файлов .cpp с несколькими вызывающими функциями, которые определены в другом исходном файле. Чтобы вызвать функцию, ее необходимо объявить, но (кроме особых случаев, таких как встроенные функции, которые не следует использовать без разбора) ни одна функция не может быть определена более одного раза во всем проекте. Использование заголовочных файлов только для объявлений позволяет включать каждый заголовочный файл в каждый исходный файл, если это необходимо. Определение функций в заголовке предотвращает включение. Peter 06.06.2020
@drescherjm Да. Приложение, над которым я работаю, состоит примерно из 600 тысяч строк кода, и если бы все было в заголовках, его сборка была бы кошмаром. В настоящее время полная сборка на машине с 20 ядрами/40 потоками занимает около 5 минут, но раньше она занимала 10+ минут, и сокращение времени компиляции в значительной степени достигалось за счет удаления элементов из заголовков и удаления ненужных включений. Это имеет огромное значение в крупных проектах. Jesper Juhl 06.06.2020
@dresherjm: GCC содержит несколько файлов C++, содержащих более дюжины тысяч строк C++. Это работает, и люди вносят в это свой вклад. Например, его gcc/go/gofrontend/expressions.cc содержит 19711 строк в GCC 10.1, и они пишутся от руки и ежедневно компилируются. Basile Starynkevitch 06.06.2020
В большом проекте вам часто нужны только объявления типов и функций, а не определения. Например, в других заголовочных файлах. Если бы все определения находились в заголовках, то совокупный результат включения нескольких других заголовков и их транзитивных включений привел бы к огромным единицам компиляции. Это значительно сокращает время компиляции, поскольку объем кода, который компилятору необходимо обработать, будет на несколько порядков больше, чем необходимо. Это также ухудшит время компоновки, поскольку у компоновщика будет больше работы по отбрасыванию дубликатов, включенных во многие другие объектные файлы.
Вы также легко столкнетесь с проблемами ODR (правило одного определения), если все не помечено inline.
Ответ 2 · рейтинг 1 · stark
В больших проектах объявления функций могут понадобиться многим файлам, но определение функции должно быть скомпилировано только один раз. Он сочетается со всеми местами, которые в нем нуждаются во время ссылки.
Ответ 3 · рейтинг 1 · Basile Starynkevitch
Небольшая программа на C++ может быть (и часто состоит) из одной единицы перевода. например несколько тысяч строк кода C++. В этом случае у вас может быть один исходный файл myprog.cc C++ (с несколькими #include внутри).
Но когда вы работаете над более крупной программой в команде, удобно иметь несколько исходных файлов C++.
Но если вы работаете в команде таких людей, как Алиса и Боб, удобно решить, что Алиса отвечает за alice.cc, а Боб пишет bob.cc, и оба совместно работают над общим заголовочным файлом header.hh, который #include-d как в alice.cc, так и в bob.cc. . Этот header.hh фактически определяет API программного проекта.
FWIW, в GCC 10.1 (от мая 2020 г.) gcc/go/gofrontend/expressions.cc написан от руки и содержит 19711 строк кода C++, то есть почти двадцать тысяч строк. Они составляются ежедневно. Я знаю людей, которые над этим работают, они замечательные и приятные профессионалы. Самый большой файл FTLK 1.4 - это src/Fl_Text_Display.cxx с 4175 строками C++.
По личному опыту, у вас может быть одна функция C++ из нескольких десятков тысяч строк C++ (это имеет практический смысл только тогда, когда этот код C++ сгенерирован), но тогда время компиляции на оптимизирующий компилятор является сдерживающим фактором. Вы можете адаптировать мою программу manydl.c для создания файлов C++. (в настоящее время он генерирует случайные файлы C с функциями настраиваемого размера) произвольного размера. Но код C++, сгенерированный Fluid или Qt Designer может быть довольно большим, а код C++, сгенерированный для GUI часто состоит из длинных, но концептуально простых функций.
Ничего в стандарте C++11 (см. n3337) требует нескольких единиц перевода. У вас может быть (для примера см. sqlite) один файл C++ foo.cc из миллиона строк. И вы можете сгенерировать часть исходного кода C++. Проект Qt, проект GCC. В книге Жака Питра Искусственные существа: совесть сознательной машины ISBN 978-1848211018 на многих страницах объясняется, почему такой подход стоит того.
Ответ 4 · Scott M
На этот вопрос есть два ответа
зачем ВАМ, программисту, хотеть разбивать файлы на несколько файлов .h/.hpp и .cpp?
Я считаю, что ответ здесь заключается в том, что это может помочь организации, когда ваши файлы .cpp становятся очень большими с большим количеством кода, который может не иметь отношения к тому, кому нужно предоставить функциональность, предоставляемую файлом. Вот пример:
Допустим, у вас есть код на С++, который отображает изображения на экране. Вы, как человек, который хочет использовать этот код, вероятно, заинтересованы в функциях/классах, предоставляемых этим кодом, которые позволяют вам управлять этой функциональностью. Возможно, код предоставляет следующие полезные функции:
WriteImageToScreen(int position_x, int position_y)
ClearScreen()
Может быть намного проще просматривать заголовочный файл, который говорит вам только о том, что вам разрешено использовать, а не о том, как все это реализовано. Вполне возможно, что для реализации этих двух функций, чтобы вы могли их вызывать, потребуется 1000 строк кода и куча переменных и операторов, которые вам не нужны. Отсутствие необходимости читать поможет вам сосредоточиться на важной части кода. Части, с которыми вы хотите взаимодействовать.
Я представил этот пример так, как если бы вы вызывали чужой код, но то же самое относится и к вашему собственному коду. По мере того, как ваши проекты становятся больше, может быть удобно иметь сводку о том, какие функции предоставляет файл.
Теперь, когда все сказано, не все согласны с тем, что это правильный способ делать что-то или что это полезно.
почему компилятору необходимо разделить вещи на несколько файлов .h/.hpp и .cpp?
На всякий случай, если вы не знакомы с этим термином, компилятор - это программа, которая превращает текст вашего исходного кода в программу, которую ваш компьютер может выполнить.
Так зачем же компилятору нужны отдельные файлы .hpp/.cpp? Другие уже в значительной степени попали в точку с этим, но компиляторы С++ путаются, если что-то определено несколько раз. Если вы поместите все в файл заголовка, то, когда вы включите этот заголовок в несколько файлов, он будет определен несколько раз. Так что по существу это возвращается к организационному вопросу.
Я видел программистов, у которых есть только один основной файл, а затем весь код включается непосредственно в этот основной файл во время компиляции.
Я считаю, что это называется сборкой монолита, и это не рекомендуется.
Ответ 5 · Pete D.
Если у вас есть игрушечный проект, вы можете.
Если у вас есть 1 000 000 строк кода, время сборки будет ужасным.
C++20 вводит модули, которые должны решить всю проблему.
В других языках есть инструменты, которые могут извлекать интерфейс из "модуля". Будем надеяться, что когда появится C++20, инструменты станут доступны.
Единственная веская причина отделить интерфейс от реализации - это наличие нескольких реализаций для одного интерфейса. Например. VHDL и будет доступен в модулях C++20. Прагматические причины заключаются в скорости компиляции и удобочитаемости.