Несколько уточню ситуацию. Сначала идёт компиляция всех c/cpp (и не только - что укажешь компилятору) файлов. При компиляции по исходнику проходит препроцессор и на место #include подставляет содержимое подключаемых файлов. Таким образом и main.cpp и file.cpp будут содержать вначале file.h с описанием класса. На выходе для каждой единицы компиляции класса получается объектный файл с скомпилированным кодом, где вместо адресов функций указывается какую функцию надо поставить при линковке (модифицированное компилятором имя функции, отражающее перечень аргументов ф-ции, способ вызова и прочее см.
name mangling).
При линковке все указанные обьектные файлы, библиотеки (просто контейнер для обьектых файлов) собираются в один исполняемые файл - вот тут уже линкер и находит что из main.obj из некой функции вызывается фйнкция, из file.obj, и подставляет нужный вызов.
По сути .h файлы это сахар, что-бы много раз не писать в каждом компилируемом модуле forward declaration чего либо, и компилятор вообще может ни чего не знать о существовании тех же .h файлов, если препроцессор реализован как отдельная программа (см. больше про C препроцессор).