Урок №21. Заголовочные файлы



Книга Урок №21. Заголовочные файлы
С постоянным увеличением объема программного кода становится все сложнее умещать его в нескольких файлах. Постоянно повторять предварительные объявления для функций, которые мы хотим использовать, но которые находятся в других файлах, становится все более утомительным. Было бы замечательно, если бы все эти предварительные объявления можно было хранить в одном месте, не так ли?

В проектах не только используются файлы .cpp, но также присутствуют заголовочные файлы (или "заголовки"), которые имеют расширение .h. Основная задача заголовочных файлов заключается в удобном хранении объявлений объектов для их дальнейшего использования в других программах.

Заголовочные файлы из Стандартной библиотеки C++

Давайте изучим следующий код:

#include

int main ( )

{

std :: cout << "Hello, world!" << std :: endl ;

return 0 ;

}

Результат работы программы:

Привет, мир!

В данной программе мы используем cout без явного определения. Как компилятор понимает, что это за объект? Ответ прост - cout объявлен в заголовочном файле iostream. При использовании директивы #include мы фактически запрашиваем копирование всего содержимого файла iostream в наш файл. Таким образом, весь функционал библиотеки iostream становится доступным для нашей программы.

Обычно в заголовочных файлах содержатся только объявления функций, без их определений. Таким образом, если cout объявлен только в заголовочном файле iostream, то где находится его определение? Ответ: определение cout находится в стандартной библиотеке C++, которая автоматически подключается к вашему проекту на этапе линковки.

Размышляйте о том, какие проблемы могут возникнуть, если заголовочный файл iostream отсутствует. При каждом использовании cout вам придется вручную копировать все необходимые объявления в начало вашего файла! Как хорошо, что можно просто добавить #include , не так ли?

Пишем свои собственные заголовочные файлы

Давайте вернемся к примеру, который мы рассматривали на прошлом занятии. У нас имелись два файла: add.cpp и main.cpp.

Файл add.cpp:

int add ( int x , int y )

{

return x + y ;

}

Файл main.cpp содержит следующий код:

#include

int add ( int x , int y ) ; // предварительное объявление с использованием прототипа функции

int main ( )

{

std :: cout << "The sum of 3 and 4 is " << add ( 3 , 4 ) << std :: endl ;

return 0 ;

}

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

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

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

Создание собственного заголовочного файла - это не такая уж и сложная задача. Заголовочные файлы обычно состоят из двух основных частей:

Расширение .h должно быть у каждого вашего заголовочного файла, который был создан вами лично.

Файл add.h содержит следующий код:

// Начнем с директив препроцессора. ADD_H – это произвольное уникальное имя (обычно используется имя заголовочного файла)

#ifndef ADD_H

#define ADD_H

// А это уже содержимое заголовочного файла

int add ( int x , int y ) ; // прототип функции add() (не забывайте точку с запятой в конце!)

// Заканчиваем директивой препроцессора

#endif

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

В файле main.cpp мы включаем заголовочный файл add.h:

#include

#include "add.h"

int main ( )

{

std :: cout << "The sum of 3 and 4 is " << add ( 3 , 4 ) << std :: endl ;

return 0 ;

}

Файл add.cpp остается неизменным:

Функция add принимает два целых числа x и y и возвращает их сумму.

Открывающий тег

возвращать x + y ;

Закрывающий тег

При обнаружении директивы #include "add.h" компилятор копирует содержимое файла add.h в текущий файл, что позволяет получить предварительное объявление функции add().

Обратите внимание: При включении заголовочного файла, все его содержимое будет вставлено непосредственно после строки include ... .

Если вам выдается сообщение об ошибке компилятора, указывающее на отсутствие файла add.h, убедитесь, что название вашего файла действительно "add.h". Возможно, вы допустили опечатку, например, написав просто "add" (без ".h") или "add.h.txt" или "add.hpp".

Если вам выдается сообщение об ошибке от линкера, указывающее на отсутствие определения функции add(), убедитесь, что вы правильно добавили файл add.cpp в ваш проект (и в процесс компиляции)!

Угловые скобки (<>) vs. Двойные кавычки («»)

Хотите узнать, зачем используются угловые скобки для iostream и двойные кавычки для add.h? При использовании угловых скобок мы даем понять компилятору, что подключаемый заголовочный файл не написан нами (он является "системным" и предоставляется Стандартной библиотекой C++), поэтому его следует искать в системных директориях. А двойные кавычки указывают компилятору, что мы подключаем собственный заголовочный файл, который мы написали сами, поэтому его нужно искать в текущей директории нашего проекта. Если файл не будет найден там, компилятор начнет искать его в других путях, включая системные директории.

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

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

Почему iostream пишется без окончания .h?

Часто возникает вопрос: "Почему при подключении iostream (или других стандартных заголовочных файлов) не указывается расширение ".h"?". Ответ кроется в том, что существуют два разных файла: iostream.h (заголовочный файл) и просто iostream! Для более подробного объяснения необходимо немного вернуться в прошлое.

Когда C++ только появился, все файлы библиотеки Runtime имели расширение .h. Оригинальные версии cout и cin были объявлены в iostream.h. При стандартизации языка C++ комитетом ANSI было решено переместить все функции из библиотеки Runtime в пространство имен std, чтобы избежать возможных конфликтов имен с пользовательскими идентификаторами (что, кстати, было хорошей идеей). Однако возникла проблема: если все функции перенести в пространство имен std, то старые программы перестанут работать!

Для сохранения совместимости ввели новый набор заголовочных файлов с такими же именами, но без расширения «.h». Весь их функционал доступен в пространстве имен std. Это позволило сохранить работоспособность старых программ, использующих include , и позволило новым программам использовать include .

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

Кроме того, многие библиотеки, унаследованные от языка Cи и до сих пор используемые в C++, были также продублированы с добавлением префикса "c" (например, stdlib.h стал cstdlib). Функционал этих библиотек также был перемещен в пространство имен std, чтобы избежать возможных конфликтов имен с пользовательскими идентификаторами.

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

Можно ли записывать определения в заголовочных файлах?

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

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

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

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

Советы

В данной статье представлены несколько рекомендаций по созданию индивидуальных заголовочных файлов:

149   0  

Comments

    Ничего не найдено.