Различия можно раскидать условно на две стопки: различия технологические и человеческий фактор.
Главный технический фактор: это можно сделать не всегда. Отделение объявлений от реализации - это важная часть того, как работает компиляция на C++. Если у вас есть циклическая зависимость в объявлениях, вы будете вынуждены иногда использовать forward-declaration, а иногда - полное описание класса. Вот как раз вариант с отдельными файлами для объявления и реализации дает решение этого вопроса - в файле .h вы работаете с forward-declaration, в файле .cpp - с полным объявлением.
(Да, есть альтернативный вариант (предложен
@user7860670) при помощи ювелирной расстановки #include, но человеческий фактор мешает лично мне рассматривать его всерьез, так это требует от разработчика следования строгим правилам, иначе - матомный взрыв из нетривиальных ошибок в исполнении компилятора).
Также утверждается, что это катастрофически сокращает время сборки и размер выходного файла, что выглядит перспективно, но достигается ценой головной боли разработчиков.
Лично на мой взгляд, размер выходного файла и время сборки - это минорные факторы, а вот тот факт, что разработчик занят выверкой имен и разгребанием ошибок, вместо решения прикладной задачи - это главный шоустоппер такого решения. Ферма из тредрипперов под сборку обойдется дешевле времени разработчиков и реабилитации их после нудной работы по приведению хидеров в порядок.
Далее уже всякая лирика:
Технически, тела всего что засунуто в объявление класса, будут расцениваться компилятором как inline, следовательно он будет пытаться, по возможности, затолкать эти тела прямо на место, откуда они вызываются - в зависимости от настроек оптимизации, разумеется. Это может повлиять на скорость работы программы и на ее размер, причем в любую сторону.
Тела отдельно стоящих функций же, вы будете вынуждены пометить как inline, иначе компилятор работать откажется.
Теперь кратко про человеческий фактор - это уже вопрос привычки и вкуса.