Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Прикладное программирование.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
439.97 Кб
Скачать

5.2. Виртуальные функции. Абстрактные классы

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

Виртуальные функции – функции базового класса, которые можно заместить в каждом производном классе. Если базовый класс вызывает перегруженную виртуальную функцию, то всегда будет вызываться функция наследника.

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

virtual void print() const;

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

void main ()

{

// виртуальные функции важны, когда создаются указатели на класс

Detail *p=new Lens;

}

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

Реализация функции в базовом классе не изменится, а классе-наследнике и реализация и описание функции останется прежним.

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

Detail *p=new Lens;

При этом:

  • Если должны вызываться "родные" функции-члены классов, то используется обычная перегрузка

l.print(); // Detail::print

  • Если функция-член базового класса должна подменяться функцией-членом класса-наследника, то при ее объявлении используется модификатор virtual

l.print(); // Lens::print

l.print(); // Lens::print

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

Detail::print();

Делать виртуальными можно практически все функции, за исключением конструкторов и оператора =. Особый интерес представляют виртуальные деструкторы. Виртуальным деструктор делают, если для правильного осовобождения памяти необходимо, чтобы деструктор всегда вызывался для класса-наследника.

5.2.2. Абстрактные классы

Некоторые базовые классы (например, класс Detail) представляют собой абстрактную концепцию, для которой не могут существовать экземпляры. Невозможно нарисовать абстрактную деталь или выполнить расчет прохождения луча через деталь.

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

Абстрактным называется класс, имеющий чисто виртуальные функции. Чисто виртуальная функция – это функция, которая не определена в базовом классе, и обязательно должна быть перегружена в классах наследниках. Если какая-то абстрактная функция не будет перегружена в классе наследнике – компилятор выдаст сообщение об ошибке.

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

Рассмотрим как будет выглядеть описание абстрактного базового класса Деталь из примера 5.1. Реализация базоваго класса, и описание и реализация классов-наследников не изменятся.

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

// класс Деталь - базовый класс для всех оптических деталей

class Detail

{

protected:

// координата детали по оси z

double m_z;

// диаметр детали

double m_D;

public:

// конструкторы и деструктор

Detail();

Detail(double z, double D);

virtual ~Detail();

// установить диаметр детали

void Set_D(double D);

// получить диаметр детали

double Get_D() const;

// установить координату по оси z

void Set_z(double z);

// получить координату по оси z

double Get_z() const;

// печать параметров детали

virtual void print() const;

// вычисление хода луча через деталь

// должно быть реализовано во всех классах-наследниках

virtual void RayTrace() = 0;

};

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

Тестирующая функция для абстрактного класса:

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

void main()

{

// массив указателей на детали

vector<Detail*> details(2);

// первый элемент - линза

details[0] = new Lens;

// второй элемент - зеркало

details[1] = new Mirror;

// печать всех деталей

cout<<"-------------------------"<<endl;

for(size_t i=0; i<details.size(); ++i)

{

details[i]->print();

}

// расчет хода луча через все детали

cout<<"-------------------------"<<endl;

for(size_t i=0; i<details.size(); ++i)

{

details[i]->RayTrace();

}

// освобождение памяти для всех деталей (вызов деструкторов)

cout<<"-------------------------"<<endl;

for(size_t i=0; i<details.size(); ++i)

{

delete details[i];

}

}

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