@s2sk

Описание класса в .cpp?

Извиняюсь за столь глупый вопрос и не желание рыться в гугле...

Смотрите... Создаю два файла -

Class.h
Class.cpp

Сам класс в .h, а его описание в .cpp. А потом к основному main.cpp подключаю просто файл .h. Я правильно понимаю?
Но вот только как Class.cpp увидит класс в Class.h? Я получается должен еще в самом Class.cpp подключить Class.h. Так ли это? Получается какой-то костыль. И вообще кто придумал так делать? Это же ужас как не удобно... Чтобы создать новую функцию нужно лезть в один файл, потом описывать ее в другом файле... При этом если функция имеет много аругментов... Почему нельзя все сразу в .h файле описать и потом его подключить к main.cpp?
  • Вопрос задан
  • 3308 просмотров
Решения вопроса 3
@MarkusD Куратор тега C++
все время мелю чепуху :)
Такс, давай отойдем немного назад, ведь у тебя явно ощущаются пробелы в знаниях.

Сам класс в .h, а его описание в .cpp.

Чтобы тебя правильно понимали, сперва нужно понять общую терминологию, а далее - этой терминологией нужно корректно пользоваться.
У нас есть два строгих термина: Declaration (объявление) и Definition (определение).
Замечу одну важную вещь - для определения характерно ODR (правило однократного определения), но пока на это можно не отвлекаться, потом надо будет.

А потом к основному main.cpp подключаю просто файл .h

А для чего ты это делаешь? Какую цель ты преследуешь, когда сперва объявляешь свой тип в отдельном файле, а потом говоришь препроцессору включить этот файл в тело исходного кода?
Один раз я уже отвечал на подобный вопрос, повторю только место, где начинается важная сейчас информация: "С точки зрения компилятора есть только один формат файла - формат исходного кода, который ему и надо обработать".

Компилятору известно только понятие Translation Unit. Он ничего не понимает во всех этих .h/.inl/.inc/.c/.cpp/...
Для правильной сборки кода ты должен рассказать компилятору о том, как из всей понятной только тебе бурды собрать вожделенный для него модуль трансляции, в котором будет весь исчерпывающий код для завершения компиляции.

Тебе незачем лепить файлы только ради объявлений и определений. Все равно для компилятора это будет собрано в один файл. А такие системы сборки, как FastBuild или IncrediBuild и вовсе весть проект вгоняют в один файл и только потом кормят этим файлом компилятор.

Получается какой-то костыль.

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

Любой мелкий проект ты всегда можешь написать в одном файле на 2 килостроки. Это не страшно.
Когда у тебя более-менее большой проект, один файл у тебя уже не получится.
Появляется необходимость заниматься архитектурой кода: выводить сущности, связывать их, получать зависимости. Помимо архитектуры кода, появляется необходимость наладить грамотное разделение кода на файлы. А файлы нужно грамотно компоновать вместе. Появляется необходимость в макроархитектуре проекта.
Тут на помощь и приходят устоявшиеся правила разделения кода по типам файлов.

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

И именно поэтому, если определение сущности можно выделить в отдельный модуль трансляции, то это лучше сделать. Если тип явным образом не используется в других модулях проекта, его лучше загнать в файл исходного кода. Если функция не предназначена для встраивания, ее лучше загнать в файл исходного кода. Если есть частная специализация шаблона, которая используется только в одном файле исходного кода, то лучше там ее и определить.
В этом случае процесс сборки твоего большого проекта будет легким и быстрым.
Ответ написан
Комментировать
tsarevfs
@tsarevfs Куратор тега C++
C++ developer
Все правильно. Class.h подключается к Class.cpp.
И да, это, в своем роде, костыль. Дело в том, что #include работает как текстовая подстановка (просто копипастит содержимое файла вместо себя).
Если несколько cpp файлов будут в себя включать определение одного и того же класса, или функции, то на этапе линковки(отдельно скомпилированные cpp склеиваются вместе) возникнет ошибка про повторное определение.
Ответ написан
Комментировать
Adamos
@Adamos
Можно и в .h-файле все сделать. Правда, обычно такие файлы выделяют расширением .hpp
Во-первых, при перекомпиляции не обязательно перекомпилировать весь проект. Только измененные файлы. Вы исправили что-то внутри класса? Если он в отдельном файле - перекомпилируется один файл. Если же этот файл подключен к другим - будет пересобираться вся цепочка. При разработке большого проекта это очень критично.
Во-вторых, библиотека может поставляться в виде готовых скомпилированных модулей, из исходников - только .h-файлы
В целом, это хорошая практика - в заголовочном файле - то, что об этом классе должны знать другие (его интерфейс и особенности реализации, влияющие на то, как он будет подключен), а в другом файле - его "потроха", которые уже никого не касаются.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы