Это пошло еще из Си. Абсолютно тот же механизм, что используется для глобальных переменных и функций. Более того в ранних версиях Си надо было всегда отдельно объявлять функцию и отдельно ее определять.
Используется для этих вещей:
1) Можно иметь "циклические зависимости". Например, функция использует класс, а класс использует функцию. Или два класса используют друг друга. В каком порядке их определять в файле? Так уж повелось, что компилятору надо как-то знать о всех используемых в коде штуках до их использования (в основном). Возможность отдельно объявить функцию/класс как раз решает эту проблему. Можно было бы переделать весь компилятор, чтобы он сначала по всему файлу искал функции, но по каким-то, возможно веским, причинам это не было сделано еще в Си. А C++ потащил и это с собой.
2) Разделение программы на "модули" или библиотеки. Этот ужасный, устаревший и неудобный механизм include-ов позволяет разбить программу на хедеры и файлы с реализацией. Потом хедеры только с объявлениями включаются в другие файлы, чтобы компилятор знал о коде в других модулях, а реализация собирается в отдельный объектный файл. В конце линкер собирает все объектные файлы в исполняемый файл и/или библиотеки. Если бы объявления и определения не были разделены, то весь код в проекте компилировался бы кучу раз в каждом объектном модуле. Это просто глупо.
3) И вообще - это красиво. Иметь где-то аккуратный список всех методов класса (или функций в библиотеке) удобно, когда вам надо разобраться, а что этот класс делает и как его использовать. Если бы там еще и код методов был, то это было бы сложнее читать.