17.4.3. Альтернативная иерархия классов

17.4.3. Альтернативная иерархия классов

Хотя наша иерархия классов Query представляется вполне приемлемой, она вовсе не является единственно возможной. Например, AndQuery и OrQuery связаны с бинарной операцией, поэтому они в какой-то степени дублируют друг друга. Можно вынести все данные и функции-члены, общие для них, в абстрактный базовый класс BinaryQuery. Поддерево новой иерархии Query изображено на рисунке 17.2:

Query

BinaryQuery

AndQuery OrQuery

Рис. 17.2. Альтернативная иерархия классов

Класс BinaryQuery - это тоже абстрактный базовый класс, следовательно, его фактические объекты в приложении не появляются. Разумной реализации eval() для него предложить нельзя, поэтому чисто виртуальная функция, объявленная в Query, в классе BinaryQuery останется чисто виртуальной. (Подробнее о таких функциях мы поговорим в разделе 17.5.)

Две функции-члена для доступа - lop() и rop(), общие для обоих классов, переносятся выше, в BinaryQuery, и определяются как нестатические встроенные. Аналогично два члена _lop и _rop, объявленные в обоих классах, также переносятся в BinaryQuery и становятся нестатическими и защищенными. Открытые конструкторы обоих производных классов объединяются в один защищенный конструктор BinaryQuery:

class BinaryQuery : public Query {

public:

const Query *lop() { return _lop; }

const Query *rop() { return _rop; }

protected:

BinaryQuery( Query *lop, Query *rop )

: _lop( lop ), _rop( rop )

{}

Query *_lop;

Query *_rop;

};

Складывается впечатление, что теперь оба производных класса должны предоставить лишь подходящие реализации eval():

// увы! эти определения классов некорректны

class OrQuery : public BinaryQuery {

public:

virtual void eval();

};

class AndQuery : public BinaryQuery {

public:

virtual void eval();

};

Однако в том виде, в котором мы их определили, эти классы неполны. При компиляции самих определений ошибок не возникает, но если мы попытаемся определить фактический объект:

// ошибка: отсутствует конструктор класса AndQuery

AndQuery proust( new NameQuery( "marcel" ),

new NameQuery( "proust " ));

то компилятор выдаст сообщение об ошибке: в классе AndQuery нет конструктора, готового принять два аргумента.

Мы предположили, что AndQuery и OrQuery наследуют конструктор BinaryQuery точно так же, как они наследуют функции-члены lop() и rop(). Однако производный класс не наследует конструкторов базового. (Это могло бы привести к ошибкам, связанным с неинициализированными членами производного класса. Представьте, что будет, если в AndQuery добавить пару членов, не являющихся объектами классов: унаследованный конструктор базового класса для инициализации объекта производного AndQuery применять уже нельзя. Однако программист может этого не осознавать. Ошибка проявится не при конструировании объекта AndQuery, а позже, при его использовании. Кстати говоря, перегруженные операторы new и delete наследуются, что иногда приводит к аналогичным проблемам.)

Каждый производный класс должен предоставлять собственный набор конструкторов. В случае классов AndQuery и OrQuery единственная цель конструкторов - обеспечить интерфейс для передачи двух своих операндов конструктору BinaryQuery. Так выглядит исправленная реализация:

// правильно: эти определения классов корректны

class OrQuery : public BinaryQuery {

public:

OrQuery( Query *lop, Query *rop )

: BinaryQuery( lop, rop ) {}

virtual void eval();

};

class AndQuery : public BinaryQuery {

public:

AndQuery( Query *lop, Query *rop )

: BinaryQuery( lop, rop ) {}

virtual void eval();

};

Если мы еще раз взглянем на рис. 17.2, то увидим, что BinaryQuery - непосредственный базовый класс для AndQuery и OrQuery, а Query -для BinaryQuery. Таким образом, Query не является непосредственным базовым классом для AndQuery и OrQuery.

Конструктору производного класса разрешается напрямую вызывать только конструктор своего непосредственного предшественника в иерархии (виртуальное наследование является исключением из этого правила, да и из многих других тоже: см. раздел 18.5). Например, попытка включить конструктор Query в список инициализации членов объекта AndQuery приведет к ошибке.

При определении объектов классов AndQuery и OrQuery теперь вызываются три конструктора: для базового Query, для непосредственного базового класса BinaryQuery и для производного AndQuery или OrQuery. (Порядок вызова конструкторов базовых классов отражает обход дерева иерархии наследования в глубину.) Дополнительный уровень иерархии, связанный с BinaryQuery, практически не влияет на производительность, поскольку мы определили его конструкторы как встроенные.

Так как модифицированная иерархия сохраняет открытый интерфейс исходного проекта, то все эти изменения не сказываются на коде, который был написан в расчете на старую иерархию. Хотя модифицировать пользовательский код не нужно, перекомпилировать его все же придется, что может отвратить некоторых пользователей от перехода на новую версию.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

2.6 Иерархия каталогов

Из книги автора

2.6 Иерархия каталогов В первой главе рассмотрение иерархии файловой системы, начиная с каталога /usr/you, носило несколько неформальный характер. Теперь мы хотим изучить ее последовательно, начиная от корня дерева.Корневой каталог называется /:$ ls /binbootdevetclibtmpunixusr$Программа


23.4.8. Иерархия виджитов

Из книги автора

23.4.8. Иерархия виджитов GtkObject+GtkWidget| +GtkMisc| | +GtkLabel| | | +GtkAccelLabel| | | `GtkTipsQuery| | +GtkArrow| | +GtkImage| | `GtkPixmap| +GtkContainer| | +GtkBin| | | +GtkAlignment| | | +GtkFrame| | | | `GtkAspectFrame| | | +GtkButton| | | | +GtkToggleButton| | | | | `GtkCheckButton| | | | | `GtkRadioButton| | | | `GtkOptionMenu| | | +GtkItem| | | | +GtkMenuItem| | | | | +GtkCheckMenuItem| | | | | | `GtkRadioMenuItem| | | | | `GtkTearoffMenuItem| | | | +GtkListItem|


Альтернативная навигация

Из книги автора

Альтернативная навигация Наша практика показала, что наличие альтернативной навигации в каталоге товаров сильно упрощает работу посетителей.Помимо классического разбиения товаров на категории и подкатегории можно предлагать навигацию по брендам, ценам, популярности,


Иерархия компонентов в IBX

Из книги автора

Иерархия компонентов в IBX Поскольку вы работаете с Delphi (или с C++ Builder), то предполагается, что вы знакомы с объектно-ориентированным программированием Таким образом, разобравшись, как именно и от кого унаследованы различные компоненты IBX, можно будет более полно


Иерархия VBA

Из книги автора

Иерархия VBA Теперь, после знакомства с приведенным выше примером программы, вам легче будет понять следующие определения и описания строительных блоков программного кода VBA.* Оператор - это наименьшая, способная выполняться единица VBA-кода. Оператор может объявлять или


4.2.1. Иерархия класса геометрии

Из книги автора

4.2.1. Иерархия класса геометрии Классы геометрии определяют свою иерархию следующим образом:Geometry (non-instantiable)Point (instantiable)Curve (non-instantiable)LineString (instantiable)LineLinearRingSurface (non-instantiable)Polygon (instantiable)GeometryCollection (instantiable)MultiPoint (instantiable)MultiCurve (non-instantiable)MultiLineString (instantiable)MultiSurface (non-instantiable)MultiPolygon


19.2.8. Иерархия классов исключений в стандартной библиотеке C++

Из книги автора

19.2.8. Иерархия классов исключений в стандартной библиотеке C++ В начале этого раздела мы определили иерархию классов исключений, с помощью которой наша программа сообщает об аномальных ситуациях. В стандартной библиотеке C++ есть аналогичная иерархия, предназначенная для


Пример 16-5. Альтернативная форма перенаправления в цикле while

Из книги автора

Пример 16-5. Альтернативная форма перенаправления в цикле while #!/bin/bash# Это альтернативный вариант предыдущего сценария.# Предложил: by Heiner Steven#+ для случаев, когда циклы с перенаправлением#+ запускаются в субоболочке, из-за чего переменные, устанавливаемые в цикле,#+ не


1.7. Иерархия файлов

Из книги автора

1.7. Иерархия файлов Вся информация, хранящаяся на жестком диске вашего Мака, упорядочена иерархическим способом. Иерархическая организация является многоуровневой, при которой каждый новый уровень следует из предыдущего от высшего к низшему. На самом первом (верхнем)


Открытая или частная иерархия

Из книги автора

Открытая или частная иерархия Ключевой концепцией для понимания того, следует ли организации выбирать инсорсинг или аутсорсинг, является идея открытых и частных иерархий [105]. Открытыми называют иерархии, подчиненные корневому УЦ, открытый ключ которого встроен в