17.5.5. Виртуальные деструкторы

17.5.5. Виртуальные деструкторы

В данной функции мы применяем оператор delete:

void doit_and_bedone( vector Query* *pvec )

{

// ...

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;

}

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