for ( ; it != end_it; ++it )

{

Query *pq = *it;

// ...

delete pq;

}

}

Чтобы функция выполнялась правильно, применение delete должно вызывать деструктор того класса, на который указывает pq. Следовательно, необходимо объявить деструктор Query виртуальным:

class Query {

public:

virtual ~Query() { delete _solution; }

// ...

};

Деструкторы всех производных от Query классов автоматически считаются виртуальными. doit_and_bedone() выполняется правильно.

Поведение деструктора при наследовании таково: сначала вызывается деструктор производного класса, в случае pq - виртуальная функция. По завершении вызывается деструктор непосредственного базового класса - статически. Если деструктор объявлен встроенным, то в точке вызова производится подстановка. Например, если pq указывает на объект класса AndQuery, то

delete pq;

приводит к вызову деструктора класса AndQuery за счет механизма виртуализации. После этого статически вызывается деструктор BinaryObject, а затем - снова статически - деструктор Query.

В следующей иерархии классов

class Query {

public: // ...

protected:

virtual ~Query();

// ...

};

class NotQuery : public Query {

public:

~NotQuery();

// ...

};

уровень доступа к конструктору NotQuery открытый при вызове через объект NotQuery, но защищенный - при вызове через указатель или ссылку на объект Query. Таким образом, виртуальная функция подразумевает уровень доступа того класса, через объект которого вызывается:

int main()

{

Query *pq = new NotQuery;

// ошибка: деструктор является защищенным

delete pq;

}

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

<p>17.5.6. Виртуальная функция eval()</p>

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

class Query {

public:

virtual void eval() = 0;

// ...

};

Реальное разрешение имени eval() происходит при построении отображения слов на вектор позиций. Если слово есть в тексте, то в отображении будет его вектор позиций. В нашей реализации вектор позиций, если он имеется, передается конструктору NameQuery вместе с самим словом. Поэтому в классе NameQuery функция eval() пуста.

Однако мы не можем унаследовать чисто виртуальную функцию из Query. Почему? Потому что NameQuery - это конкретный класс, объекты которого разрешается создавать в приложении. Если бы мы унаследовали чисто виртуальную функцию, то он стал бы абстрактным классом, так что создать объект такого типа не удалось бы. Поэтому мы объявим eval() пустой функцией:

class NameQuery : public Query {

public:

virtual void eval() {}

// ...

};

Для запроса NotQuery отыскиваются все строки текста, где указанное слово отсутствует. Для таких строк в член _loc класса NotQuery помещаются все пары (строка, колонка). Наша реализация выглядит следующим образом:

void NotQuery::eval()

{

// вычислим операнд

_op-eval();

// _all_locs - это вектор, содержащий начальные позиции всех слов,

// он является статическим членом NotQuery:

// static const vectorlocations* _all_locs

vector location ::const_iterator

iter = _all_locs-begin(),

iter_end = _all_locs-end();

// получить множество строк, в которых операнд встречается

setshort *ps = _vec2set( _op-locations() );

// для каждой строки, где операнд не найден,

// скопировать все позиции в _loc

for ( ; iter != iter_end; ++iter )

{

if ( ! ps-count( (*iter).first )) {

_loc.push_back( *iter );

}

}

}

Перейти на страницу:

Похожие книги