|
Программирование >> Инициализация объектов класса, структура
Query *pery = retrieveQuery(); то была бы вызвана реализация print() из класса NameQuery. Обращение cout << *pquery << endl; приводит к вызову той функции print() , которая ассоциирована с объектом, адресуемым указателем pquery в данной точке выполнения программы. 17.5.2. Чисто виртуальные функции С точки зрения кодирования основная задача, стоящая перед нами в связи с поддержкой пользовательских запросов, - это реализация зависимых от типа операций для каждого из возможных операторов. Для этого мы определили четыре конкретных типа классов: AndQuery, OrQuery и т.д. Однако с точки зрения проектирования наша цель -инкапсулировать обработку каждого вида запроса, спрятать за не зависящим от типа интерфейсом. Это позволит построить ядро приложения, которое не потребует изменений при добавлении или удалении типов. Чтобы добиться этого, определим абстрактный тип класса Query. При этом мы не будем программировать разные типы пользовательских запросов, а лишь абстрактные void doit and bedone( vector< Query* > *pvec ) { vector<Query*>::iterator it = pvec->begin(), end it = pvec->end(); for ( ; it != end it; ++it ) Query *pq = *it; cout << обрабатывается << *pq << endl; pq->eval(); pq->display(); delete pq; операции, применимые к ним: cout << query << endl; вызывают наш оператор вывода в ostream, который в свою очередь вызывает q.print( os ) где q привязано к объекту query класса AndQuery, а os - к cout. Если бы вместо этого NameQuery query2( Salinger ); мы написали: cout << query2 << endl; class Query { public: объявляется чисто виртуальная функция virtual ostreams print( ostream&=cout ) const = 0; ... Объявляются они следующим образом: Заметьте, что за объявлением функции следует присваивание нуля. Класс, содержащий (или наследующий) одну или несколько таких функций, распознается компилятором как абстрактный базовый класс. Попытка создать независимый объект абстрактного класса приводит к ошибке компиляции. (Ошибкой является также вызов В классе Query объявлен! одна или несколько виртуальн функций, поэтому программист не может создавать независимхе объекты асса Query класса правильно: подобъект Query в составе NameQuery Query *pq = new NameQuery( Nostromo ); ошибка: оператор new создает объект класса Query чисто виртуальной функции с помощью механизма виртуализации.) Например: Query *pq2 = new Query; Абстрактный базовый класс может существовать только как подобъект в составе объекта некоторого производного от него класса. Это именно та семантика, которая нужна нам для базового Query. Такое определение позволяет добавлять неограниченное число типов запросов без необходимости изменять или даже перекомпилировать ядро системы, но при условии, что открытый интерфейс нашего абстрактного базового класса Query достаточен для поддержки новых запросов. Проектируя открытый интерфейс Query, мы определим множество операций, достаточное для поддержки всех существующих и будущих типов запросов, хотя на практике нам вряд ли удастся это гарантировать. Предоставление общего интерфейса для тех запросов, о которых мы уже знаем, - вполне реальная задача, но любое заявление, претендующее на более широкую поддержку, следует рассматривать с долей скептицизма. Поскольку Query - абстрактный класс, объекты которого в приложении не создаются, то никакой разумной реализации виртуальных функций в нем самом мы предложить не можем. Это лишь названия, которые должны быть замещены в производных классах. Напрямую вызывать их мы не будем. Язык обладает синтаксической конструкцией, обозначающей, что некоторая виртуальная функция предоставляет интерфейс, который должен быть замещен в производных подтипах, но вызываться непосредственно не может. Это чисто виртуальные функции. Query *pery = new NameQuery( dumo ); isA() вызается динамически с помощью механизма виртуазации реально будет вызвана NameQuery::isA() pquery->isA(); isA вызывается статически во время компиляции реально будет вызвана Query::isA и каждом из производных классов иерархии Query: pquery->Query::isA(); Тогда явный вызов Query: :isA() разрешается на этане компиляции в пользу реализации isAO в базовом классе Query, хотя pquery адресует объект NameQuery. Зачем нужно отменять механизм виртуализации? Как правило, ради эффективности. В теле виртуальной функции производного класса часто необходимо вызвать реализацию из базового, чтобы завершить операцию, расщепленную между базовым и производным классами. К примеру, вполне вероятно, что виртуальная функция display() из Camera выводит некоторую информацию, общую для всех камер, а реализация display() в классе PerspectiveCamera сообщает информацию, специфичную только для перспективных камер. Вместо того чтобы дублировать в ней действия, общие для всех камер, можно вызвать реализацию из класса Camera. Mi точно знаем, какая именно реализация нам нужна, поэтому нет нужд: прибегать к механизму виртуализации. Более того, реализация в Camera объявлена встроенной, так что разрешение во время компиляции приводит к подстановке по месту вызова. Приведем еще один пример, когда отмена механизма виртуализации может оказаться полезной, а заодно познакомимся с неким аспектом чисто виртуальных функций, который начинающим программистам кажется противоречащим интуиции. Реализации функции print() в классах AndQuery и OrQuery совпадают во всем, кроме литеральной строки, представляющей название оператора. Реализуем только одну функцию, которую можно вызывать из данных классов. Для этого мы снова определим абстрактный базовый BinaryQuery (его наследники - AndQuery и OrQuery). В нем определены два операнда и еще один член тина string для хранения значения оператора. Поскольку это абстрактный класс, объявим print() чисто виртуальной функцией: 17.5.3. Статический вызов виртуальной функции Вызывая виртуальную функцию с помощью оператора разрешения области видимости класса, мы отменяем механизм виртуализации и разрешаем вызов статически, на этапе компиляции. Предположим, что мы определили виртуальную функцию isA() в базовом
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |