|
Программирование >> Синтаксис инициирования исключений
возвращаемый объект оператором new (вместо стека) решает проблему, поскольку величина заведомо остается жить после завершения функции. Но тогда возникают проблемы с управлением памятью - когда и как удалять возвращаемую величину? Чтобы решить ее, нам понадобится материал глав 11 и 1 2, а также одна из методик управления памятью (скажем, подсчет ссылок), рассматриваемых в последней части книги. Итак, приведенной в этой главе информации хватит для решения простых проблем (например, связанных с событиями и видами), но она лишь закладывает основу для построения более общих решений. Производящие функции и объекты классов В предыдущей главе мы стролкнулись с гомоморфными иерархиями классов и с головой погрузились во множественную передачу. В этой главе мы продолжим исследовать царство иерархий классов, рассматривая объекты классов и сопутствующие темы. Примеры из предыдущей главы обладали одним недостатком - все производные классы были видны клиентам. Но если производные классы погребены в файлах .cpp на глубине нескольких метров, как клиенту создавать экземпляры этих спрятанных классов? Этой теме посвящено начало главы. Производящей функцией (factory function) называется функция, которая инкапсулирует применение оператора new для создания экземпляров класса. Знатоки С++ обычно сходятся на том, что производящие функции - Хорошая Вещь, и вскоре вы поймете почему. Вы оказались на семинаре с коктейлями и хотите найти хорошую тему для разговора? Поднимите стакан и небрежно упомяните о том, как производящие функции выручили вас в трудную минуту. Во многих программах нам хотелось бы в процессе их выполнения сделать нечто, не поддерживаемое динамической моделью С++ (например, запросить у объекта его класс). Для этой цели существует предложенный стандарт RTTI (Run Time Type Information, Динамическая информация о типе), но по причинам, о которых будет сказано ниже, его вряд ли можно считать универсальным или хотя бы полезным средством. Вместо этого мы рассмотрим нестандартные решения, основанные на концепции объектов классов, в том числе особый случай - представителей классов. Производящие функции Предположим, вы согласились, что гомоморфизм - это хорошо, и тут же сотворили свою собственную гомоморфную иерархию классов. В файле Grandpa.h class Grandpa { ... }; Скрыто в файле Grandpa.cpp class Dad : public Grandpa { ... }; class AuntieEm : public Grandpa { ... }; Где-то в нашей программе #inc1ude Grandpa.h Grandpa* g = new ... Стоп! Как создать папу ? Допустим, с позиций биологии все понятно, но мы говорим о С++, не правда ли? Проблема заключается в том, что мы надежно изолировали папу (Dad) от внешнего мира - по крайней мере для любого кода, расположенного за пределами файла Grandpa.cpp. Замечательный интерфейс Grandpa позволяет нам как угодно манипулировать любым экземпляром производного класса, включая Dad, но при это не существует способа создать этот экземпляр! make-функции На сцену выходят производящие функции. По общепринятому соглашению их простейшая форма называется makeFoo(), где Foo - имя генерируемого класса. class Grandpa { public: static Grandpa* makeDad(); Создает экземпляры Dad static Grandpa* makeAuntieEm(); В Grandpa.cpp Grandpa* Grandpa::makeDad() return new Dad; Grandpa* Grandpa::makeAuntieEm() return new AuntieEm; О существовании конкретных производных классов по-прежнему известно всем, однако настоящие интерфейсы Dad и AuntieEm надежно спрятаны от любопытных глаз. Символические классы и перегруженные make-функции Все эти функции makeFoo расходятся с перегружаемой природой С++, не правда ли? Следующий фрагмент помогает выбрать нужную версию make. class Variation Dad {}; Пустое объявление класса class VariationAuntieEm {}; class Grandpa { public: static Grandpa* make(VariationDad); static Grandpa* make(VariationAuntieEm); В вашей программе Grandpa* g = Grandpa::make(VariationDad()); Вызов VariationDad() создает анонимный экземпляр (anonymous instance) класса VariationDad - то есть экземпляр, не связанный ни с какой переменной в исходной области действия. Он живет лишь столько, сколько необходимо для вызова, а затем исчезает. Экземпляр VariationDad нужен лишь для одного - он сообщает компилятору, какая перегруженная версия make должна вызываться. Класс, который не имеет членов и используется подобным образом, называется символическим классом (symbol class). Он играет ту же роль, что и символический тип данных в языках типа Lisp: строка, которая заранее компилируется в более эффективную форму. Поскольку производные классы все равно инкапсулированы в файле .cpp, мы можем воспользоваться этим обстоятельством и заменить имя исходного Dad другим локальным по отношению к файлу .cpp, а затем просто воспользоваться Dad вместо VariationDad в файле .h. Оптимизация с применением производящих функций Задержимся на минутку. Допустим, ни один из производных классов Grandpa не интересует публику настолько, чтобы различать их при выборе производящей функции. Даже в этом случае
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |