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