Урок 122. Классы и заголовочные файлы

  Юрий Ворон  | 

    | 

  Обновлено 8 Мар 2018  | 

 5247

 ǀ   2 

Все классы, которые мы использовали до сих пор, были достаточно простыми, и мы писали методы непосредственно внутри классов. Например:

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

К счастью, C++ предоставляет способ отделить «объявление» от «реализации». Это делается путем определения методов вне самого класса. Для этого просто определите методы класса, как если бы они были обычными функциями, но в качестве префикса добавьте к имени функции имя класса с оператором разрешения области видимости : : (то же, что используется с пространствами имен).

Вот наш класс Date с конструктором Date и методом setDate(), определенными вне тела класса. Обратите внимание, прототипы этих функций все еще находятся внутри класса, но их фактическая реализация находится за его пределами:

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

Вот еще один пример класса с конструктором, определенным извне, со списком инициализации членов:

конвертируем в:



Классы и заголовочные файлы

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

Вот наш Date, но уже разбитый на файлы .cpp и .h:

Date.h:

Date.cpp:

Теперь любой другой файл .h или .cpp, который захочет использовать класс Date, сможет просто #include "Date.h". Обратите внимание, Date.cpp также необходимо добавить к компиляции в проект, который использует Date.h, чтобы линкер смог разобраться с реализацией класса Date.

Разве определение класса в заголовочном файле не нарушает правило одного определения?

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

Разве определение методов класса в заголовочном файле не нарушает правило одного определения?

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

Методы, определенные вне класса, рассматриваются как обычные функции и подчиняются правилу одного определения. Поэтому эти функции должны быть определены в файле .cpp, а не внутри .h. Единственное исключение – шаблоны функций, но об этом уже в следующей главе.

Итак, что я должен определять в заголовочном файле и что в файле .cpp, а также что внутри класса, а что вне класса?

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

  Во-первых, как упоминалось выше, это приведет к загромождению определения вашего класса.

  Во-вторых, функции, определенные внутри класса, являются неявно встроенными. Большие функции, которые вызываются из многих файлов, могут способствовать, таким образом, к «раздуванию» вашего кода.

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

Поэтому мы рекомендуем следующее:

  Классы, используемые только в одном файле, и которые повторно не используются, определяйте непосредственно в файле .cpp, где они используются.



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

  Тривиальные методы (обычные конструкторы или деструкторы, функции доступа и т.д.) определяйте внутри класса.

  Нетривиальные методы определяйте в файле .cpp с тем же именем, что у класса.

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

Параметры по умолчанию

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

Библиотеки

Разделение объявления класса и его реализации очень распространено с библиотеками, которые используются для расширения возможностей вашей программы. Вы также подключали заголовочные файлы из стандартной библиотеки, такие как iostream, string, vector, array и другие. Обратите внимание, вы не добавляли iostream.cpp, string.cpp, vector.cpp или array.cpp в ваши проекты. Ваша программа нуждается только в объявлениях из заголовочных файлов, чтобы компилятор мог проверить корректность вашего кода в соответствии с правилами синтаксиса C++. Однако реализации классов, находящихся в стандартной библиотеке, содержатся в предварительно скомпилированном файле, который добавляется на этапе линкинга. Вы никогда не видите этот код.

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

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

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

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

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (24 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 2

  1. Oleksiy:

    Не понятно, что означает символ "&" в этом выражении:

    Это ссылка на функцию add? Тогда почему значек стоит в конце Matem, а не в начале add?

    1. Keyrus:

      Это значит, что функция add возвращает значение типа "ссылка на тип Mathem".

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

telegram канал
RAVESLI