Такс, давай отойдем немного назад, ведь у тебя явно ощущаются пробелы в знаниях.
Сам класс в .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 тут еще встает вопрос избыточной компиляции кода - когда ненужные объявления все равно включаются в исходный код и впустую тратят процессорное время на свою трансляцию. В этом случае становится важно не включать лишние объявления или ненужные определения в заголовочные файлы.
И именно поэтому, если определение сущности можно выделить в отдельный модуль трансляции, то это лучше сделать. Если тип явным образом не используется в других модулях проекта, его лучше загнать в файл исходного кода. Если функция не предназначена для встраивания, ее лучше загнать в файл исходного кода. Если есть частная специализация шаблона, которая используется только в одном файле исходного кода, то лучше там ее и определить.
В этом случае процесс сборки твоего большого проекта будет легким и быстрым.