В оставшейся части этого раздела мы последовательно изучим конструктор базового класса и конструкторы четырех производных от него, а после этого рассмотрим альтернативный дизайн иерархии классов Query, чтобы познакомиться с иерархиями глубиной больше двух. В конце раздела речь пойдет о деструкторах классов.
17.4.1. Конструктор базового класса
В нашем базовом классе объявлено два нестатических члена: _solution и _loc:
class Query {
public:
// ...
protected:
setshort *_solution;
vectorlocation _loc;
// ...
};
Конструктор Query по умолчанию должен явно инициализировать только член _solution. Для инициализации _loc автоматически вызывается конструктор класса vector. Вот реализация нашего конструктора:
inline Query::Query(): _solution( 0 ) {}
В Query нам понадобится еще один конструктор, принимающий ссылку на вектор позиций:
inline
Query::
Query( const vector locaton &loc )
: _solution( 0 ), _loc( loc )
{}
Он вызывается только из конструктора NameQuery, когда объект этого класса используется для представления указанного в запросе слова. В таком случае передается предварительно подготовленный для него вектор позиций. Остальные три производных класса вычисляют свои векторы позиций в соответствующей функции-члене eval(). (В следующем подразделе мы покажем, как это делается. Реализации функций-членов eval() приведены в разделе 17.5.)
Какой уровень доступа обеспечить для конструкторов? Мы не хотим объявлять их открытыми, так как предполагается, что Query будет существовать в программе только в виде подобъекта в составе объектов производных от него классов. Поэтому мы объявим конструктор не открытым, а защищенным:
class Query {
public:
// ...
protected:
Query();
// ...
};
Ко второму конструктору класса Query предъявляются еще более жесткие требования: он не только должен конструировать Query в виде подобъекта производного класса, но этот производный класс должен к тому же быть NameQuery. Можно объявить конструктор закрытым, а NameQuery сделать другом класса Query. (В предыдущем разделе мы говорили, что производный класс может получить доступ только к открытым и защищенным членам базового. Поэтому любая попытка вызвать второй конструктор из классов AndQuery, OrQuery или NotQuery приведет к ошибке компиляции.)
class Query {
public:
// ...
protected:
Query();
// ...
private:
explicit Query( const vectorlocation& );
};
(Необходимость второго конструктора спорна; вероятно, правильнее заполнить _loc в функции eval() класса NameQuery. Однако принятый подход в большей степени отвечает нашей цели проиллюстрировать использование конструктора базового класса.)
17.4.2. Конструктор производного класса
В классе NameQuery также определены два конструктора. Они объявлены открытыми, поскольку ожидается, что в приложении будут создаваться объекты этого класса:
class NameQuery : public Query {
public:
explicit NameQuery( const string& );
NameQuery( const string&, const vector* );
// ...
protected:
// ...
};
Конструктор с одним параметром принимает в качестве аргумента строку. Она передается конструктору объекта типа string, который вызывается для инициализации члена _name. Конструктор по умолчанию базового класса Query вызывается неявно:
inline
NameQuery::
NameQuery( const string &name )
// Query::Query() вызывается неявно
: _name( name )
{}
Конструктор с двумя параметрами также принимает строку в качестве одного из них. Второй его параметр - это указатель на вектор позиций. Он передается закрытому конструктору базового класса Query. (Обратите внимание, что _present нам больше не нужен, и мы исключили его из числа членов NameQuery.)
inline
NameQuery::
NameQuery( const string &name, vector& location& *ploc )
: _name( name ), Query( *ploc )
{}
Конструкторы можно использовать так:
string title( "Alice " );
NameQuery *pname;
// проверим, встречается ли "Alice " в отображении слов
// если да, получить ассоциированный с ним вектор позиций
if ( vector location *ploc = retrieve_location( title ))
pname = new NameQuery( title, ploc );
else pname = new NameQuery( title );