|
Программирование >> Полиморфизм без виртуальных функций в с++
displayed task displayed task Рис. 12.1 Я не считаю такой стиль представления списка идеальны.м во всех ситуациях, но иногда он оптимален. Поэтому С++ поддерживает приведенный выше пример. По у.молчанию базовый класс, появляющийся в иерархии наследования дважды, будет представлен дву.мя подобъекта.ми. Однако возможно и другое разрешение ситуации [Stroustrup, 1987]: Я называю это независимым множественным наследованием. Но во многих случаях применения множественного наследования предполагается наличие зависимости между базовыми классами (например, предоставление каких-то возможностей для окно). Такие зависимости можно выразить с помощью объекта, разделяемого различными производными классами. Другими словами, требуется указать, что базовый класс должен привносить лишь один объект в конечный в этом смысле С++ отличается от объектно-ориентированных диалектов Lisp, поддерживающих множественное наследование [Stroustrup, 1987]. Разрешение неоднозначности с помощью зависимости от порядка, когда предпочтение отдается А: : f просто потому, что А встречается раньше в, я отверг, так как имел негативный опыт использования подобных зависи.мостей в других местах (см. разделы 11.2.2. и 6.3.1). Я отказался также от всех форм динамического разрешения, помимо виртуальных функций, поскольку они не годятся для статически типизированного языка с жесткими требования.ми к эффективности. 12.3. Виртуальные базовые классы Вот цитата из работы [Stroustrup, 1987]: Клосс может встречаться в направленном ациклическом графе наследования более одного раза: class task : public link { /* ... */ }; class displayed : public link { /* ... */ } ; class displayed task : public displayed, public task { /* ... */ } ; В данном случае объект класса displayed task имеет два подобъекта класса link: task: : link и displayed: : link. Часто это бывает полезно, например в случае реализации списков, когда требуется, чтобы каждый элемент списка содержал поле связи. В результате объект класса displayed task находится одновременно в списках объектов displayed и task . Это .можно представить и графически, показав, из каких подобъектов состоит displayed task (см. рис. 12.1). link link Какие классы могут выступать в роли W, AW, BW и CW в реальных программах? Первоначальным примером была простая оконная система, основанная на идеях, почерпнутых из литературы по Lisp, см. рис. 12.3. window window with border window with menu window wi th menu and border Рис. 12.3 На Moii взгляд, этот пример искусственный, но он взят из реальной программы и, что особенно важно для представления, интуитивно понятен. Несколько примеров можно найти и в стандартной библиотеке потокового ввода/вывода [Shopiro, 1989] (см. рис. 12.4). производный класс, доже если он встречается в иерархии наследования несколько роз. Чтобы отличить этот случай от независимого множественного наследования, такие базовые классы описываются как виртуальные: class AW: public virtual W { /* ... */ } ; class BW: public virtual W {/*...*/ }; class CW: public AW, public BW { /* ... */ }; Единственный объект класса W будет разделяться но AW и BW; иными словами, в CW должен быть включен только один объект W, хотя CW наследует и AW, и BW. Если не считать того, что в производном классе возникает всего один объект, виртуальный базовый класс ведет себя точно ток же, как обыкновенный. Виртуальность W - это свойство наследования, которое задается для AW и BW, о не для самого W. Все виртуольные базовые классы в графе наследования относятся к одному и тому же объекту . Графически это представлено на рис. 12.2. istream ostream ifstream ofstream fstream Рис. 12.4 Мне неизвестно, почему виртуальные базовые классы следует считать более полезными или фундаментальными, чем обыкновенные, и наоборот. Поэтому С++ поддерживает те и другие. По умолчанию решено было принять обыкновенный базовый класс, так как на его реализацию, в отличие от виртуального, тратится .меньше времени и памяти; к тому же программирование с помощью виртуальных базовых классов несколько сложнее, че.м с помощью обыкновенных. Проблема в том, чтобы избежать множественных вызовов функции из виртуального класса, когда это нежелательно [2nd], см. также раздел 12.5. В связи с трудностями реализации возникло искушение не включать в язык понятие виртуального базового класса. Однако я счел решающим довод о необходимости какого-то способа представления зависимостей между потомками одних родителей ( братьев ). Классы- братья могут взаимодействовать между собой только через общий корневой класс, через глобальные данные или через явные указатели. Если бы ие было виртуальных базовых классов, то необходимость существования общего корня привела бы к чрезмерно широкому использованию у1шверсальных базовых классов. Смешанный стиль, описанный в разделе 12.3.1, дает пример такого взаимодействия братьев . Если уж поддерживать множественное наследование, то подобный механизм необходим. С другой стороны, наиболее полезным я считаю простое применение множественного наследования вроде определения класса, который обладает суммой атрибутов двух других независимых классов. 12.3.1. Виртуальные базовые классы и виртуальные функции Сочетание абстрактных и виртуальных базовых классов предназначалось для поддержки стиля програ.ммирования, который был бы приблизительно эквивалентен смешанному стилю (mixin style) в некоторых системах на Lisp. Это означает, что абстрактный класс определяет интерфейс, а реализацию предоставляет ряд производных классов. Каждый производный класс (компонент - mixin) делает вклад в полный класс (смесь - mix).
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |