C++ Программирование в среде С++ Builder 5

Виртуальные функции


Функции-элементы класса могут объявляться в C++ как виртуальные. Ключевое слово virtual заставляет компилятор генерировать для класса некоторую дополнительную информацию 6 функции. Происходит следующее: если виртуальная функция переопределяется в производном классе, и если имеется указатель или ссылка на базовый класс (которые могут с тем же успехом ссылаться на производный класс, поскольку производный объект есть в то же время и объект базового класса), то при обращении к функции через указатель (ссылку) будет вызвана правильная функция-элемент (т. е. соответствующая типу действительного объекта) — базового или одного из производных классов, в зависимости от типа конкретного объекта.

Не хочу показаться занудой, но, как мне кажется, стоит повторить, чем косвенное обращение к объекту (указатель или ссылка) в данном отношении отличается от прямого. Можно недвусмысленно объявить объект базового и объект производного классов. Потом можно присвоить объект производного класса переменной базового типа. Не требуется даже никаких приведений, потому что, как я уже говорил, производный объект является объектом базового класса. “Автомобиль” есть “средство передвижения”. Однако при этом будет потеряна всякая специфика “автомобиля”, отличающая его от всех других средств передвижения, наземных, водных или воздушных. Но применение указателей или ссылок в объектно-ориентированных языках типа C++ приводит к тому, что объект сам может помнить, к какому типу он относится, и указатель на базовый тип может быть в данном случае снова приведен

Следующий пример покажет вам разницу между виртуальным и не виртуальным переопределением функции.

//////////////////////////////////////////////////////////

// Virtual.cpp: Демонстрация виртуальной функции.

//

#pragma hdrstop

#include <condefs.h>

#include <stdio.h>

class One

{



// Базовый класс. public:

virtual void ShowVirtO

// Виртуальная функция.

{

printf("It's One::ShowVirt()!\n");

}

void ShowNonVirt() // Не-виртуальная функция.


{

printf("It's One::ShowNonVirt()!\n") ;

}

};

class Two: public One

{

// Производный класс. public:

virtual void ShowVirt()

(

printf ("It's Two::ShowVirtO !\n") ;

)

void ShowNonVirt ()

(

printf("If s Two::ShowNonVirt ()!\n") ;

) };

int main(void)

{

Two derived;

One *pBase = sderived;

pBase->ShowVirt(); // Вызовет Two::ShowVirt().

pBase->ShowNonVirt(); // Вызовет One::ShowNonVirt().

//

// Следующий вызов подавляет виртуальный механизм:

// pBase->One::ShowVirt();

// Явно вызывает One::ShowVirt().

return 0;

}

Результат работь! программы (рис. 8.4) показывает, что при обращении к виртуальной функции через базовый указатель будет'вызвана “правильная” функция, соответствующая типу действительного (производного) объекта.

Ключевое слово virtual при объявлении функции в производных классах не обязательно. Функция, однажды объявленная виртуальной, остается таковой во всех производных классах иерархии.



Рис. 8.4 Демонстрация виртуальной и не-виртуальной функции



Виртуальная функция не может быть статической.

Желательно, а иногда просто необходимо, объявлять виртуальным деструктор базового класса. Если этого не сделать, то при удалении динамического объекта, на который ссылается указатель базового класса, всегда будет вызываться только базовый деструктор вне зависимости от того, чем является данный объект. Это можно проиллюстрировать такой схемой:



class One { public:

~One () { /* ... */ }

};

class Two: public One

{

Something *s;

public:

Two()

{

s = new Something; // Выделение ресурса.

}

~Two()

{

delete s; // Очистка. } };

int main() {

One *pBase = new Two;

// ...

delete pBase; // Удаление динамического объекта.

return 0;

}



В данном примере при удалении объектаоперацией delete будет вызван только базовый деструктор ~0nе (), хотя объект принадлежит к производному классу. Чтобы вызывался правильный деструктор, следовало бы объявить его виртуальным в базовом классе:



virtual ~One() { /* ... */}


Содержание раздела