Я думаю, здесь есть путаница. Проблема не в заголовках. Заголовки ничего не делают (это всего лишь способы включить общие биты исходного текста в несколько файлов исходного кода).
Проблема, так же как и проблема, состоит в том, что объявления классов в C ++ должны определять все, публичное и приватное, что должно иметь экземпляр для работы. (То же самое относится и к Java, но из-за того, что ссылки на классы, скомпилированные извне, работают, нет необходимости использовать что-либо вроде общих заголовков.)
В сущности общих объектно-ориентированных технологий (не только C ++) необходимо, чтобы кто-то знал конкретный класс, который используется, и как использовать его конструктор для реализации, даже если вы используете только общественные части. Устройство в (3, ниже) скрывает это. Практика в (1, ниже) разделяет проблемы, независимо от того, делаете ли вы (3) или нет.
Используйте абстрактные классы, которые определяют только открытые части, в основном методы, и пусть класс реализации наследуется от этого абстрактного класса. Таким образом, используя обычное соглашение для заголовков, существует abstract.hpp, который используется совместно. Существует также реализация .hpp, которая объявляет унаследованный класс и передается только тем модулям, которые реализуют методы реализации. В файле реализаций .hpp будет #include «abstract.hpp» для использования в объявлении класса, которое он делает, так что существует единственная точка обслуживания для объявления абстрактного интерфейса.
Теперь, если вы хотите принудительно скрыть объявление класса реализации, у вас должен быть какой-то способ запросить конструкцию конкретного экземпляра, не имея специального, полного объявления класса: вы не можете использовать new, и вы не может использовать локальные экземпляры. (Вы можете удалить, хотя.) Введение вспомогательных функций (включая методы в других классах, которые доставляют ссылки на экземпляры классов) является заменой.
Наряду с или как часть заголовочного файла, который используется в качестве общего определения для абстрактного класса / интерфейса, включите сигнатуры функций для внешних вспомогательных функций. Эти функции должны быть реализованы в модулях, которые являются частью конкретных реализаций классов (чтобы они видели полное объявление класса и могли использовать конструктор). Сигнатура вспомогательной функции, вероятно, очень похожа на сигнатуру конструктора, но в результате она возвращает ссылку на экземпляр (этот прокси-конструктор может возвращать указатель NULL и даже может генерировать исключения, если вам нравится такая вещь). Вспомогательная функция создает конкретный экземпляр реализации и возвращает его в качестве ссылки на экземпляр абстрактного класса.
Миссия выполнена.
Да, и перекомпиляция и перекомпоновка должны работать так, как вы хотите, избегая перекомпиляции вызывающих модулей, когда изменяется только реализация (поскольку вызывающий модуль больше не выполняет выделение памяти для реализаций).