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