Как вынести функции в отдельный файл c
Когда мы пишем программу на C/C++ в одном файле, проблем обычно не возникает. Они ждут того момента, когда исходный текст необходимо разбить на несколько файлов. В этой статье я постараюсь рассказать, как это сделать правильно.
Термины
Пара слов о терминах. Ниже даны определения терминов так, как они используются в данной статье. В некоторых случаях эти определения имеют более узкий смысл, чем общепринятые. Это сделано намеренно, дабы не утонуть в деталях и лишних уточнениях.
Исходный код — программа, написанная на языке программирования, в текстовом формате. А также текстовый файл, содержащий исходный код.
Компилятор — программа, выполняющая компиляцию (неожиданно! не правда ли?). На данный момент среди начинающих наиболее популярными компиляторами C/C++ являются GNU g++ (и его порты под различные ОС) и MS Visual Studio C++ различных версий. Подробнее см. в Википедии статьи: Компиляторы, Компиляторы C++.
Компиляция — преобразование исходного кода в объектный модуль.
Объектный модуль — двоичный файл, который содержит в себе особым образом подготовленный исполняемый код, который может быть объединён с другими объектными файлами при помощи редактора связей (компоновщика) для получения готового исполняемого модуля, либо библиотеки. (подробности)
Компоновщик (редактор связей, линкер, сборщик) — это программа, которая производит компоновку («линковку», «сборку»): принимает на вход один или несколько объектных модулей и собирает по ним исполнимый модуль. (подробности)
Исполняемый модуль (исполняемый файл) — файл, который может быть запущен на исполнение процессором под управлением операционной системы. (подробности)
Препроцессор — программа для обработки текста. Может существовать как отдельная программа, так и быть интегрированной в компилятор. В любом случае, входные и выходные данные для препроцессора имеют текстовый формат. Препроцессор преобразует текст в соответствии с директивами препроцессора. Если текст не содержит директив препроцессора, то текст остаётся без изменений. Подробнее см. в Википедии: Препроцессор и Препроцессор Си.
IDE (англ. Integrated Development Environment) — интегрированная среда разработки. Программа (или комплекс программ), предназначенных для упрощения написания исходного кода, отладки, управления проектом, установки параметров компилятора, линкера, отладчика. Важно не путать IDE и компилятор. Как правило, компилятор самодостаточен. В состав IDE компилятор может не входить. С другой стороны с некоторыми IDE могут быть использованы различные компиляторы. (подробности)
Объявление — описание некой сущности: сигнатура функции, определение типа, описание внешней переменной, шаблон и т.п. Объявление уведомляет компилятор о её существовании и свойствах.
Определение — реализация некой сущности: переменная, функция, метод класса и т.п. При обработке определения компилятор генерирует информацию для объектного модуля: исполняемый код, резервирование памяти под переменную и т.д.
От исходного кода к исполняемому модулю
Создание исполняемого файла издавна производилось в три этапа: (1) обработка исходного кода препроцессором, (2) компиляция в объектный код и (3) компоновка объектных модулей, включая модули из объектных библиотек, в исполняемый файл. Это классическая схема для компилируемых языков. (Сейчас уже используются и другие схемы.)
Часто компиляцией программы называют весь процесс преобразования исходного кода в исполняемы модуль. Что неправильно. Обратите внимание, что в IDE этот процесс называется построение (build) проекта.
IDE обычно скрывают три отдельных этапа создания исполняемого модуля. Они проявляются только в тех случаях, когда на этапе препроцессинга или компоновки обнаруживаются ошибки.
Итак, допустим, у нас есть программа на C++ «Hello, World!»:
Затем результат работы препроцессора передаётся компилятору. Компилятор производит весь положенный комплекс работ: от синтаксического разбора и поиска ошибок до создания объектного файла (понятно, что если имеются синтаксические ошибки, то объектный файл не создаётся). В объектном файле обычно имеется таблица внешних ссылок — некая таблица, в которой, в частности, перечислены имена подпрограмм, которые используются в объектном модуле, но код которых отсутствует в данном объектном модуле. Эти подпрограммы внешние по отношению к модулю.
Исходный код, который может быть откомпилирован, называется единицей компиляции. Наша программа содержит одну единицу компиляции.
Что бы получить нормальный исполняемый модуль, необходимо «разрешить» внешние ссылки. Т.е. добавить в исполняемый модуль код отсутствующих подпрограмм и настроить соответствующим образом все ссылки на этот код. Этим занимается компоновщик. Он анализирует таблицу внешних ссылок объектного модуля, ищет в объектных библиотеках недостающие модули, копирует их в исполняемый модуль и настраивает ссылки. После этого исполняемый модуль готов.
Библиотека (объектная библиотека) — это набор откомпилированных подпрограмм, собранных в единый файл определённой структуры. Подключение библиотеки происходит на этапе компоновки исполняемого файла из объектных файлов (т.е. из тех файлов, которые получаются в результате компиляции исходного текста программы).
Необходимые объектные библиотеки входят в комплект поставки компилятора. В комплект поставки библиотек (любых) входит набор заголовочных файлов, которые содержат объявления, необходимые компилятору.
Если исходный код программы разделён на несколько файлов, то процесс компиляции и сборки происходит аналогично. Сначала все единицы компиляции по отдельности компилируются, а затем компоновщик собирает полученные объектные модули (с подключением библиотек) в исполняемый файл. Собственно, этот процесс и называется раздельной компиляцией.
Разделение текста программы на модули
Разделение исходного текста программы на несколько файлов становится необходимым по многим причинам:
- С большим текстом просто неудобно работать.
- Разделение программы на отдельные модули, которые решают конкретные подзадачи.
- Разделение программы на отдельные модули, с целью повторного использования этих модулей в других программах.
- Разделение интерфейса и реализации.
Я намеренно использовал слово «модуль», поскольку модулем может быть как класс, так и набор функций — вопрос используемой технологии программирования.
Как только мы решаем разделить исходный текст программы на несколько файлов, возникают две проблемы:
- Необходимо от простой компиляции программы перейти к раздельной. Для этого надо внести соответствующие изменения либо в последовательность действий при построении приложения вручную, либо внести изменения в командные или make-файлы, автоматизирующие процесс построения, либо внести изменения в проект IDE.
- Необходимо решить каким образом разбить текст программы на отдельные файлы.
Первая проблема — чисто техническая. Она решается чтением руководств по компилятору и/или линкеру, утилите make или IDE. В самом худшем случае просто придётся проштудировать все эти руководства. Поэтому на решении этой проблемы мы останавливаться не будем.
Вторая проблема — требует гораздо более творческого подхода. Хотя и здесь существуют определённые рекомендации, несоблюдение которых приводит либо к невозможности собрать проект, либо к трудностям в дальнейшем развитии проекта.
Во-первых, нужно определить какие части программы выделить в отдельные модули. Что бы это получилось просто и естественно, программа должна быть правильно спроектирована. Как правильно спроектировать программу? — на эту тему написано много больших и правильных книг. Обязательно поищите и почитайте книги по методологии программирования — это очень полезно. А в качестве краткой рекомендации можно сказать: вся программа должна состоять из слабо связанных фрагментов. Тогда каждый такой фрагмент может быть естественным образом преобразован в отдельный модуль (единицу компиляции). Обратите внимание, что под «фрагментом» подразумевается не просто произвольный кусок кода, а функция, или группа логически связанных функций, или класс, или несколько тесно взаимодействующих классов.
Во-вторых, нужно определить интерфейсы для модулей. Здесь есть вполне чёткие правила.
Интерфейс и реализация
Когда часть программы выделяется в модуль (единицу компиляции), остальной части программы (а если быть точным, то компилятору, который будет обрабатывать остальную часть программы) надо каким-то образом объяснить что имеется в этом модуле. Для этого служат заголовочные файлы.
Таким образом, модуль состоит из двух файлов: заголовочного (интерфейс) и файла реализации.
Заголовочный файл, как правило, имеет расширение .h или .hpp, а файл реализации — .cpp для программ на C++ и .c, для программ на языке C. (Хотя в STL включаемые файлы вообще без расширений, но, по сути, они являются заголовочными файлами.)
Заголовочный файл должен содержать все объявления, которые должны быть видны снаружи. Объявления, которые не должны быть видны снаружи, делаются в файле реализации.
Что может быть в заголовочном файле
Правило 1. Заголовочный файл может содержать только объявления. Заголовочный файл не должен содержать определения.
То есть, при обработке содержимого заголовочного файла компилятор не должен генерировать информацию для объектного модуля.
Единственным «исключением» из этого правила является определение метода в объявлении класса. Но по стандарту языка, если метод определён в объявлении класса, то для этого метода используется инлайновая подстановка. Поэтому, такое объявление не порождает исполняемого кода — код будет генерироваться компилятором только при вызове этого метода.
Аналогичная ситуация и с объявлением переменных-членов класса: код будет порождаться при создании экземпляра этого класса.
Правило 2. Заголовочный файл должен иметь механизм защиты от повторного включения.
Защита от повторного включения реализуется директивами препроцессора:
Заголовочный файл сам по себе не является единицей компиляции.
Что может быть в файле реализации
Файл реализации может содержать как определения, так и объявления. Объявления, сделанные в файле реализации, будут лексически локальны для этого файла. Т.е. будут действовать только для этой единицы компиляции.
Правило 3. В файле реализации должна быть директива включения соответствующего заголовочного файла.
Понятно, что объявления, которые видны снаружи модуля, должны быть также доступны и внутри.
Правило также гарантирует соответствие между описанием и реализацией. При несовпадении, допустим, сигнатуры функции в объявлении и определении компилятор выдаст ошибку.
Правило 4. В файле реализации не должно быть объявлений, дублирующих объявления в соответствующем заголовочном файле.
При выполнении Правила 3, нарушение Правила 4 приведёт к ошибкам компиляции.
Практический пример
Допустим, у нас имеется следующая программа:
main.cpp
Эта программа не является образцом для подражания, поскольку некоторые моменты идеологически неправильны, но, во-первых, ситуации бывают разные, а во-вторых, для демонстрации эта программа подходит очень неплохо.
Итак, что у нас имеется?
- глобальная константа cint , которая используется и в классе, и в main ;
- глобальная переменная global_var , которая используется в функциях func1 , func2 и main ;
- глобальная переменная module_var , которая используется только в функциях func1 и func2 ;
- функции func1 и func2 ;
- класс CClass ;
- функция main .
Вроде вырисовываются три единицы компиляции: (1) функция main , (2) класс CClass и (3) функции func1 и func2 с глобальной переменной module_var , которая используется только в них.
Не совсем понятно, что делать с глобальной константой cint и глобальной переменной global_var . Первая тяготеет к классу CClass , вторая — к функциям func1 и func2 . Однако предположим, что планируется и эту константу, и эту переменную использовать ещё в каких-то, пока не написанных, модулях программы. Поэтому прибавится ещё одна единица компиляции.
Теперь пробуем разделить программу на модули.
Сначала, как наиболее связанные сущности (используются во многих местах программы), выносим глобальную константу cint и глобальную переменную global_var в отдельную единицу компиляции.
globals.h
globals.cpp
Обратите внимание, что глобальная переменная в заголовочном файле имеет спецификатор extern . При этом получается объявление переменной, а не её определение. Такое описание означает, что где-то существует переменная с таким именем и указанным типом. А определение этой переменной (с инициализацией) помещено в файл реализации. Константа описана в заголовочном файле.
С объявлением констант в заголовочном файле существует одна тонкость. Если константа тривиального типа, то её можно объявить в заголовочном файле. В противном случае она должна быть определена в файле реализации, а в заголовочном файле должно быть её объявление (аналогично, как для переменной). «Тривиальность» типа зависит от стандарта (см. описание того стандарта, который используется для написания программы).
Также обратите внимание (1) на защиту от повторного включения заголовочного файла и (2) на включение заголовочного файла в файле реализации.
Затем выносим в отдельный модуль функции func1 и func2 с глобальной переменной module_var . Получаем ещё два файла:
funcs.h
funcs.cpp
Поскольку переменная module_var используется только этими двумя функциями, её объявление в заголовочном файле отсутствует. Из этого модуля «на экспорт» идут только две функции.
Наконец выносим в отдельный модуль класс CClass :
CClass.h
CClass.cpp
Обратите внимание на следующие моменты.
(1) Из объявления класса убрали определения тел функций (методов). Это сделано по идеологическим причинам: интерфейс и реализация должны быть разделены (для возможности изменения реализации без изменения интерфейса). Если впоследствии будет необходимость сделать какие-то методы инлайновыми, это всегда можно сделать с помощью спецификатора.
(2) Класс имеет статический член класса. Т.е. для всех экземпляров класса эта переменная будет общей. Её инициализация выполняется не в конструкторе, а в глобальной области модуля.
Классы практически всегда выделяются в отдельные единицы компиляции.
В файле main.cpp оставляем только функцию main . И добавляем необходимые директивы включения заголовочных файлов.
main.cpp
Последний шаг: необходимо изменить «проект» построения программы так, что бы он отражал изменившуюся структуру файлов исходного кода. Детали этого шага зависят от используемой технологии построения программы и используемого ПО. Но в любом случае сначала должны быть откомпилированы четыре единицы компиляции (четыре cpp-файла), а затем полученные объектные файлы должны быть обработаны компоновщиком для получения исполняемого файла.
Типичные ошибки
Ошибка 1. Определение в заголовочном файле.
Эта ошибка в некоторых случаях может себя не проявлять. Например, когда заголовочный файл с этой ошибкой включается только один раз. Но как только этот заголовочный файл будет включён более одного раза, получим либо ошибку компиляции «многократное определение символа . », либо ошибку компоновщика аналогичного содержания, если второе включение было сделано в другой единице компиляции.
Ошибка 2. Отсутствие защиты от повторного включения заголовочного файла.
Тоже проявляет себя при определённых обстоятельствах. Может вызывать ошибку компиляции «многократное определение символа . ».
Ошибка 3. Несовпадение объявления в заголовочном файле и определения в файле реализации.
Обычно возникает в процессе редактирования исходного кода, когда в файл реализации вносятся изменения, а про заголовочный файл забывают.
Если необходимый заголовочный файл не включён, то все сущности, которые в нём объявлены, останутся неизвестными компилятору. Вызывает ошибку компиляции «не определён символ . ».
Ошибка 5. Отсутствие необходимого модуля в проекте построения программы.
Ошибка 6. Зависимость от порядка включения заголовочных файлов.
Не совсем ошибка, но таких ситуаций следует избегать. Обычно сигнализирует либо об ошибках в проектировании программы, либо об ошибках при разделении исходного кода на модули.
Заключение
В рамках небольшой статьи невозможно рассмотреть все случаи, возникающие при раздельной компиляции. Бывают ситуации, когда разделение программы или большого модуля на более мелкие кажется невозможным. Обычно это бывает, когда программа плохо спроектирована (в данном случае, части кода имеют сильные взаимные связи). Конечно, можно приложить дополнительные усилия и всё-таки разделить код на модули (или оставить как есть), но эту мозговую энергию лучше потратить более эффективно: на изменение структуры программы. Это принесёт в дальнейшем гораздо большие дивиденды, чем просто силовое решение.
Как вынести функцию в отдельный файл как в PHP например:
Например есть функция
Помощь в написании контрольных, курсовых и дипломных работ здесь
Как вынести структуру в отдельный файл
Помогите, пожалуйста. Задали дополнительное задание: вынести структуру в отдельный файл. Я понятия.
Как вынести реализацию методов шаблонного класса в отдельный файл?
Как вынести реализацию методов шаблонного класса в отдельный файл, описание шаблонного класса в .h.
Вынести функцию в отдельный файл. Настроить связь
Есть небольшой код. using System; namespace Game_3._1 < class Program < .
Нужно написать в начале файла:
Имя может быть не в кавычках, а в скобках:
Скобки означают, что поиск файла осуществляется в директории, гденаходятся все стандартные файлы (например, iostream), а кавычки - что
поиск идет в директории с проектом использую Borland. А с каких строк должен начинаться подключаемый файл? Я пробовал так подключать у меня в подключаемом файле появляются какие-то ошибки например в такой строке
А с каких строк должен начинаться подключаемый файл?
С самых первых строк:
Обычно делается так: прототипы помещаются в .h файл (с использованием конструкции для защиты от множественных включений в один файл); реализации - в отдельный .cpp файл; в файле (с main()), где вызываются функции, подключается файл с прототипами (.h). Файлы .cpp должны быть включены в проект.Файл f.h:
Это не обязательно. Главное, чтобы заголовочный файл был подключён до первого использования символов, объявленных в нём (это относится к C++). Это произвольное имя?
В одном из примеров мне встретилось следующее:
__ASDF_H_ (разумеется для файла с именем asdf.h) В целом да, смысл в том чтобы не повторялись включения содержимого *.h, что там проверяется\определяется не так важно(лишь бы конфликтов имен не было), но принято аля _FILENAME_H_, сразу путаница пропадает jkrnd, да, имя произвольное, однако нужно следить за тем, чтобы оно не совпадало с другими именами. Если оно будет будет конфликтовать с именами таких символов, как переменные или функции, могут возникнуть непредвиденные ошибки, как ошибки компиляции, так и трудноуловимые логические ошибки, поскольку все вхождения таких символов будут заменены препроцессором на "ничего". А если будет конфликт с другим include guard с тем же именем, то содержимое заголовочного файла будет проигнорировано. Распространённым способом формирования имени стража включения является такой вариант: __HEADER_FILE_NAME_H__, но это, разумеется, не обязательно.
файлы *.cpp оно само найдет и оформит как нужно, подключать следует только заголовки(*.h)
Добавлено через 1 минуту
Ну, т.е. если нужный *.cpp есть в проекте (в вс 2010 по умолчанию там слева показаны файлы)
Как вынести loader style в отдельный файл
Есть стиль для отображения загрузки loader, как его вынести в отдельный файл? пробовал просто.
Как вынести класс в отдельный файл с namespace?
Добрый вечер, есть проблема, не выносится класс в отдельный файл, пишет что переопределение типа.
Как код со страницы вынести в отдельный файл?
При оптимизации сайта для смартфонов в разделе head размещается длинный код виджета <style.
Минусующим: я в курсе что такое ООП и постоянно использую его в разработке. Или из-за чего вы там минусуете с пренебрежением к подобному подходу? Как это может быть ни удивительно для вас, но проекты можно писать в том числе и используя простые функции, вместо запихивания всего и вся в различные классы. Бывает множество ситуаций, когда функцию написать удобнее, чем городить целый класс для решения небольшой проблемы.
Есть один интересный подход при разработке проектов, который мне в последнее время стал нравиться. Суть простая: когда вы пишете функции — кладёте каждую из них в свой отдельный файл. Я сейчас имею ввиду те функции, которые используются во многих местах вашего проекта, формируя таким образом некоторую "библиотеку" методов.
Мало кто поступает таким образом в современном мире. Какие же профиты от такого подхода? А вот какие.
Красивые url'ы на любую функцию проекта
Многие из вас скорее всего неоднократно отправляли друг другу ссылки на различные отрезки кода на Github'е, вроде такого. Сегодня эта ссылка указывает точно на метод под названием booleanConditional . А вот на что эта ссылка будет указывать спустя пол года — на это мы посмотрим спустя пол года :) Ну, рискну предположить, что через пол года там будет мешанина какого-то совсем другого кода, никак не связанного с booleanConditional .
Если ваша функция будет лежать в своём собственном файле, то никаких смещений со временем не случится. Даже спустя долгое время после переработки проекта, ваша ссылка, скорее всего, не потеряет своей актуальности и будет указывать на всё ту же функцию, причём указывать она будет именно на её текущее актуальное содержимое.
Разве не прелесть?
Инкапсуляция кода и документации
Теперь у вас есть целый файл для вашей функции, в котором можно чего только про неё не понаписать! Всё, что будет написано в этом файле — относится только к этой функции. Описать конструкции импорта и использования нужных библиотек для её работы? Пожалуйста. Расписать большой комментарий, который детально расскажет, как должна вести себя ваша функция? Почему нет? Написать внизу примеры использования, завести обсуждение с коллегами о том, как можно её доработать? Оставить в комментариях прошлый вариант данной функции, чтобы, при необходимости, вернуться к нему обратно? Да пожалуйста, какие проблемы: берите и пишите сколько влезет.
Наличие отдельного файла для каждой функции сильно расширяет пространство для творчества. При этом, всё будет выглядеть очень удобно и логично. Когда мы описываем большое количество функций в одном большом жирном файле, мы не можем позволить себе подобные вольности.
Удобная "история жизни" каждой функции в VCS
Все мы знаем, что современные системы контроля версий позволяют смотреть историю изменения любого файла проекта. А теперь подумайте о том, как удобно может быть иметь историю жизни каждой его функции. Это также становится возможным просто благодаря тому, что мы выделили каждой своей функции по отдельному файлу.
Удобные поиск и навигация по функциям проекта
В нашем текущем проекте всё разбито следующим образом: есть ядро проекта и его модули. У ядра есть папка functions, в которой находятся ещё пять папок. Выглядит это следующим образом:
Каждый модуль проекта имеет свои собственные функции, разбитые примерно таким же образом.
Теперь найти нужную функцию проекта стало в несколько раз проще, при этом можно использовать почти любой редактор кода: главное чтобы он умел искать по названию файла в проекте. Разделение функций на группы по их областям назначения также помогает увеличить порядок проекта. Да и просто, когда всё разложено таким образом, анализировать существующие созданные методы проекта становится намного легче.
По идее, если мы ведём речь о PHP-проекте, то на продакшене, по хорошему, надо собирать все функции в один файл и загружать его одного вместо кучи файлов. Придётся написать немного кода для решения этой проблемы производительности (если это действительно вдруг станет проблемой), но это ведь не вопрос для вас, правда, мой друг-хабравчанин?
В мире JavaScript я никаких проблем не вижу. Там уже давно все адекватные люди компилируют исходники различными готовыми инструментами в один файл.
Насчёт остальных языков программирования я не знаю.
Вот такие пироги. На мой взгляд, так работать с проектом намного приятнее. А какое мнение по данной теме имеете вы?
На этом уроке мы рассмотрим работу классов с заголовочными файлами в языке С++.
Отделение объявления от реализации
Все классы, которые мы использовали до сих пор, были достаточно простыми, поэтому мы записывали методы непосредственно внутри тела классов, например:
К счастью, язык C++ предоставляет способ отделить «объявление» от «реализации». Это делается путем определения методов вне тела самого класса. Для этого просто определите методы класса, как если бы они были обычными функциями, но в качестве префикса добавьте к имени функции имя класса с оператором разрешения области видимости ( :: ).
Вот наш класс Date с конструктором Date() и методом setDate(), определенными вне тела класса. Обратите внимание, прототипы этих функций все еще находятся внутри тела класса, но их фактическая реализация находится за его пределами:
Просто, не так ли? Поскольку во многих случаях функции доступа могут состоять всего из одной строки кода, то их обычно оставляют в теле класса, хотя переместить их за пределы класса можно всегда.
Вот еще один пример класса с конструктором, определенным извне, со списком инициализации членов:
Классы и заголовочные файлы
На уроке о заголовочных файлах в языке С++ мы узнали, что объявления функций можно поместить в заголовочные файлы, чтобы затем иметь возможность использовать эти функции в нескольких файлах или даже в нескольких проектах. Классы в этом плане ничем не отличаются от функций. Определения классов могут быть помещены в заголовочные файлы для облегчения их повторного использования в нескольких файлах или проектах. Обычно, определение класса помещается в заголовочный файл с тем же именем, что у класса, а методы, определенные вне тела класса, помещаются в файл .cpp с тем же именем, что у класса.
Вот наш класс Date, но уже разбитый на файлы .cpp и .h:
Методы, определенные внутри тела класса, считаются неявно встроенными. Встроенные функции освобождаются от правила одного определения. А это означает, что проблем с определением простых методов (таких как функции доступа) внутри самого класса возникать не должно.
Методы, определенные вне тела класса, рассматриваются, как обычные функции, и подчиняются правилу одного определения, поэтому эти функции должны быть определены в файле .cpp, а не внутри .h. Единственным исключением являются шаблоны функций (но об этом чуть позже).
Параметры по умолчанию
Параметры по умолчанию для методов должны быть объявлены в теле класса (в заголовочном файле), где они будут видны всем, кто подключает этот заголовочный файл с классом.
Библиотеки
Разделение объявления класса и его реализации очень распространено в библиотеках, которые используются для расширения возможностей вашей программы. Вы также подключали такие заголовочные файлы из Стандартной библиотеки С++, как iostream, string, vector, array и другие. Обратите внимание, вы не добавляли iostream.cpp, string.cpp, vector.cpp или array.cpp в ваши проекты. Ваша программа нуждается только в объявлениях из заголовочных файлов, чтобы компилятор смог проверить корректность вашего кода в соответствии с правилами синтаксиса языка C++. Однако реализации классов, находящихся в Стандартной библиотеке С++, содержатся в предварительно скомпилированном файле, который добавляется на этапе линкинга. Вы нигде не встречаете этот код.
Вне программ с открытым исходным кодом (где предоставляются оба файла: .h и .cpp), большинство сторонних библиотек предоставляют только заголовочные файлы вместе с предварительно скомпилированным файлом библиотеки. На это есть несколько причин:
На этапе линкинга быстрее будет подключить предварительно скомпилированную библиотеку, чем выполнять перекомпиляцию каждый раз, когда она нужна.
Наличие собственных файлов, разделенных на объявление (файлы .h) и реализацию (файлы .cpp), является не только хорошей формой содержания кода, но и упрощает создание собственных пользовательских библиотек.
Заключение
Возможно, у вас возникнет соблазн поместить все определения методов класса в заголовочный файл внутри тела класса. Хотя это скомпилируется, но здесь есть несколько нюансов:
Во-первых, как упоминалось выше, это приведет к загромождению определения вашего класса.
Во-вторых, функции, определенные внутри класса, являются неявно встроенными. Большие функции, которые вызываются из многих файлов, могут способствовать, таким образом, «раздуванию» вашего кода.
Поэтому рекомендуется следующее:
Классы, используемые только в одном файле, и которые повторно не используются, определяйте непосредственно в файле .cpp, где они используются.
Классы, используемые в нескольких файлах или предназначенные для повторного использования, определяйте в заголовочном файле с тем же именем, что у класса.
Тривиальные методы (обычные конструкторы или деструкторы, функции доступа и т.д.) определяйте внутри тела класса.
Нетривиальные методы определяйте в файле .cpp с тем же именем, что у класса.
На следующих уроках большинство наших классов будут определены в файле .cpp со всеми методами, реализованными непосредственно в теле класса. Это делается для удобства и лаконичности примеров. В реальных проектах лучше, когда классы помещаются в отдельные файлы .cpp и .h.
Читайте также: