logo
lab_rab_6

8.2. Виртуальные методы. Динамическое связывание

К механизму виртуальных функций (virtual function) обращаются в тех случаях, когда в базовый класс необходимо поместить функцию, которая должна по-разному выполняться в производных классах. Виртуальные функции важны потому, что они используются для поддержки динамического полиморфизма.

Рассмотрим следующий пример. Предположим, мы определили базовый класс Figure, который описывает любую геометрическую фигуру и у этого класса есть метод Build построения этой фигуры. Затем мы хотим создать два класса, производных от класса Figure: класс Rectangle и класс Circle, в которых мы хотим реализовать свои методы Build для построения прямоугольника и окружности соответственно. В таком случае в производных классах следует переопределить метод Build из базового класса.

Для того, чтобы метод базового класса можно было переопределять в производных необходимо выполнить следующие шаги:

1. В базовом классе заголовок метод должен содержать служебное слово virtual.

2. В производных классах переопределяемые методы должны быть записаны со служебным словом override.

Слово virtual в переводе с английского значит «фактический». Объявление метода виртуальным означает, что ссылки на этот метод будут разрешаться по факту его вызова, то есть не на стадии компиляции, а во время выполнения программы. Такой механизм называется поздним связыванием.

Если в базовом классе функция объявлена как виртуальная, то ее вызовы будут обрабатываться методом динамического (позднего) связывания. Ключевое слово virtual предписывает компилятору генерировать некоторую дополнительную информацию о функции. В листинге 8.6 приведена программа, иллюстрирующая работу с виртуальными методами.

Листинг 8.6. Переопределение виртуального метода базового класса в производных классах

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace ConsoleApplication1

{

// базовый класс Figure

class Figure

{

// определение виртуального метода Build в базовом классе Figure

public virtual void Build()

{

Console.WriteLine("Строим геометрическую фигуру");

}

}

// производный класс Rectangle

class Rectangle : Figure

{

// переопределение виртуального метода Build в производном классе //Rectangle

public override void Build()

{

Console.WriteLine("Строим прямоугольник");

}

}

// производный класс Circle

class Circle : Figure

{

// переопределение виртуального метода Build в производном классе Circle

public override void Build()

{

Console.WriteLine("Строим окружность");

}

}

class Program

{

static void Main(string[] args)

{

// создание массива из трех ссылок на объекты типа Figure

Figure[] obj = new Figure[3];

// инициализация ссылки адресом объекта ,базового класса

obj[0] = new Figure();

// инициализация ссылки адресом объекта производного класса

obj[1] = new Rectangle();

// инициализация ссылки адресом объекта производного класса

obj[2] = new Circle();

for (int i = 0; i < obj.Length; i++)

// вызывается виртуальный метод соответствующего класса

obj[i].Build();

}

}

}

Эта программа выводит следующие строки:

Строим геометрическую фигуру

Строим прямоугольник

Строим окружность

Реализация метода динамического связывания осуществляется следующим образом. Для виртуальных методов компилятор формирует таблицу виртуальных методов (Virtual Method Table, VMT). Для каждого класса создается одна таблица. Каждый объект во время выполнения программы должен иметь доступ к VMT. Обеспечение этой связи нельзя поручить компилятору, так как она должна устанавливаться во время выполнения программы при создании объекта. Поэтому связь экземпляра объекта с VMT устанавливается с помощью специального кода, автоматически помещаемого компилятором в конструктор объекта.

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

С помощью виртуальных методов реализуется один из основных принципов объектно-ориентированного программирования– полиморфизм. Это слово в переводе с греческого означает «много форм», что в данном случае означает «один вызов – много методов». Применение виртуальных методов обеспечивает гибкость и возможность расширения функциональности класса.