C++ для начинающих. Раздельная компиляция (Продолжаем Знакомство)

В программировании С++, при использовании ООП, поощряется разделение класса на отдельные единицы. В понимании новичков, эти единицы — это файлы. Чтобы использовать один класс в большом числе случаев создают аж целых три файла. Я говорю "файлы", чтобы читателю было проще понять о чем идет речь. Каждый из этих файлов является кирпичиком функциональности класса.
1. Файл — файл интерфейса
2. Файл — файл реализации
3. Нечто — Это нечто обращается к элементам реализации.

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

Это очень схематично, если описать такой класс и создать для него объект, то программа успешно скомпилируется и запустится.

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

  • Код С++ описание интерфейса класса
  • Вот, никаких int main(){} внутри интерфейса класса быть не должно. Функция main должна быть в третьей части, в части "Нечто, что использует класс"
    Под интерфейсом класса понимают такое описание класса, которое отображает суть класса. Ничего лишнего в этой части быть не должно. В текущем примере (где 5 строчек) легко увидеть всю суть класса и понять, в классе есть целочисленное значение и есть функция foo, которая возвращает что-то типа int и доступна из любых мест вне класса. Тут сейчас нет функции, есть только ее прототип, вот описание поведения функции, коей выписан прототип, выносят в отдельный файл. Все описания поведения функций для этого класса нужно записать в один отдельный файл. Файл с описанием поведений функций станет файлом реализации. Название файл-реализации как раз происходит из-за того что внутри такого файла описывают поведение функции или, можно сказать, реализовывают функции.

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

    • Файл интерфейса по некоторому соглашению сохраняют с расширением *.h
    • Файл реализации по некоторому соглашению сохраняют с расширением *.cpp

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

    То есть класс мог быть разделен на две составляющие с именами
    MyClass.h
    MyClass.cpp

    Здесь одинаковые имена MyClass, разные расширения.
    Программа, использующая подобный класс может называться как угодно, она абсолютно не зависит от класса. Она только нуждается в подключении класса при необходимости. Такую программу в некоторой литературе могут называть программой-драйвером (программа владеет классом и может использовать объекты класса и всю их функциональнось).

    Пришло время примеров. Не будем выдумать ничего неординарного. Создание классного журнала (журнал для школьных оценок). Я буду делать очень минималистическую функциональность в связи с большим объемом информации по теме.

    Требуется определится с функциональностью класса и что от него требуется. Для начала достаточно минимальной функциональности. Иначе сложно объяснить. Делаем небольшой анализ, продумываем класс:
    1. Журнал ведется по определенному предмету, значит класс должен
    — хранить название предмета
    — сохранять/устанавливать название предмета
    — должна быть возможность узнать название предмета

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

    Видите, что здесь не описано поведение функций, есть голый класс, это и есть интерфейс класса. Хорошо видно, что этот класс будет из себя представлять и в большом числе случаев читателю уже сейчас интуитивно понятно, что это будет за класс. Этот файл сохраняется как заголовочный файл с расширением *.h
    Назовем его MyGradeBook.h

    Таким образом, сейчас должен иметься один сохраненный файл с описанием интерфейса класса.
    Следующий шаг — описание поведения функций. Создается еще один файл, с таким же именем, но другим расширением MyGradeBook.cpp, в котором пишут функции. В этом файле с помощью диррективы #include подключают интерфейс класса. Т.е. это выглядит так

    • Файл MyGradeBook.cpp

    Это нужно, чтобы файл реализации что-то знал о внешнем мире. Благодаря такому подключению, файл MyGradeBook.cpp знает о существовании класса class GradeBook, больше он пока что ничего не знает. В общем, это такой вид связи файлов. Так как файл MyGradeBook.cpp знает о существовании класса, то, в нем легко можно описать функции-члены класса. Вот их в нем и описывают.

    • Файл MyGradeBook.cpp

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

    • MyGradeBook.h

    • MyGradeBook.cpp

    Вот такой минипроект. Файл MyGradeBook.cpp должен успешно компилироваться. Если он не компилируется, значит где-то допущена ошибка. Например пропущена точка с запятой внутри файла-интерфейса после описание класса (распространенная ошибка). Проверили, если что-то надо исправить исправили и убрали отсюда main, здесь этой функции быть не должно. Функция main должна быть в программе, которая будет задействовать класс.

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

    • MyGradeBook.h

    • MyGradeBook.cpp

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

    Начинаем разработку какого-то кода. При работе в IDE, создаем проект. Обратите внимание на то, что, если Вы работаете не с командной строкой, а интегрированной средой разработки, то создается именно проект. Сейчас желательно пуcтой (empty) проект. В этот проект добавляются файлы реализации *.cpp, но не заголовочные файлы *.h. Добавление файлов в разных IDE может иметь различные вариации, но все сводится приблизительно к тому, что надо найти меню проекта, в меню проекта найти добавить файлы (add files…) и добавить все желаемые в проекте файлы реализации. К сожалению в разных IDE меню может различаться, а из-за этого нельзя единогласно рассказать как и где добавлять. В VS2015 фалы можно добавлять через обозреватель решений, в CodeBlocks через view-manager и т.д. Вам прийдется разбираться со своей IDE самостоятельно. Вернемся к проекту. В нашем случае в проект нужно добавить все *.cpp файлы. Таких файлов должно быть два. Первый файл — это файл реализации функций класса, второй файл — это то нечто, нечто что-то, что этот класс станет использовать.

    Предположительно Вы повозились с Вашей IDE и разузнали как создавать проект и добавлять в него файлы. Если да, то идем дальше.

    • MyGradeBook.h

    • MyGradeBook.cpp

    • Нечто, что в чем будет использован класс 12345.cpp

    Если все было сделано правильно, то попытка собрать 12345.cpp будет успешной и программа выполнится словно она была описана в одном файле. Только в VS может потребоваться задержка, чтобы консоль сразу не закрывалась. тогда Код может выглядеть так

    • Нечто, что в чем будет использован класс 12345.cpp

    Хочу обратить внимание на подключение заголовочных файлов. Тут разделения нет, что если подключен в одном файле, то сработает везде. Если в каком-то файле проекта требуется использовать что-то, что требует использования заголовочных файлов, то тогда надо подключать такой заголовочный файл непосредственно внутри нужного файла (реализация, интерфейс, нечто). Как пример, #include + cout описан у меня в разных местах. Просто требуется указывать такие вещи на месте.

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

    • MyGradeBook.cpp

     
     

    Процесс компиляции и компоновки


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

    Коду клиента, чтобы использовать класс, необходимо знать только интерфейс GradeBook и, кроме того, он должен иметь возможность компоноваться с его объектным кодом. Поскольку интерфейс является частью определения класса в заголовочном файле GradeBook.h, программист кода клиента должен иметь доступ к этому файлу и включить его посредством #include в файл исходного кода клиента. Когда код клиента компилируется, компилятор использует определение класса в MyGradeBook.h для того, чтобы убедиться в корректности создания объектов класса GradeBook и манипуляций ими в функции main.

    Последним шагом построения исполняемого приложения является компоновка
    1. объектного кода функции main (т.е. кода клиента),
    2. объектного кода реализации элемент-функций класса GradeBook,
    3. объектного кода Стандартной библиотеки C++ для классов C++ (например, string), применяемых программистом класса и программистом кода клиента.

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

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

    Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

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

    Ваш e-mail не будет опубликован.

    Поиск

     
         
    Яндекс.Метрика

    НАГРАДИ АВТОРА САЙТА
    WEBMONEY
    R375024497470
    U251140483387
    Z301246203264
    E149319127674
    
    
    Демотиватор никогда не сдаваться

    Выражаю свою признательность

    • Максиму очень признателен за указание на мои ошибки и неточности.
    • Sergio ===> за оказание помощи в исправлении моих ошибок
    • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок