Объектно-ориентированное программирование на C++

       

Указатели на базовые классы


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

Point pointObj (100,20); // объект базового класса

Circle circleObj (20,30,10); // объект производного класса

Point *pointPtr; // указатель базового класса

pointPtr = & pointObj; // указывает на объект базового класса

pointPtr = & circleObj; // указывает на объект производного класса

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

pointPtr = & pointObj;

pointPtr->MoveTo(10,10);

Таким же образом можно обратиться и к методу объекта производного класса.

pointPtr = & circleObj;

pointPtr -> Expand(12);

Еще больший интерес представляет вызов виртуальных методов, используя этот указатель.

pointPtr = & pointObj;

pointPtr->Show(10,10); // вызов Show() объекта pointObj класса Point

pointPtr = & circleObj;

pointPtr->Show(10,10); // вызов Show() объекта circleObj класса Circle

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

Рассмотрим некоторую процедуру JumpFigure(), имитирующую "скачек" некоторого объекта на экране на заданную высоту h.

void JumpFigure (Point* AnyFigure, int h)

{

int oldX = AnyFigure->GetX();

int oldY = AnyFigure->GetY();

delay(100);  // временная задержка на 0.1 сек

AnyFigure->MoveTo (oldX, oldY-h); // "прыжок"

delay(100);  // временная задержка на 0.1 сек

AnyFigure->MoveTo (oldX, oldY); // на исходную позицию

}

Функции JumpFigure() можно передавать указатель на любой объект типа Point, либо на объект класса, порожденного из класса Point.

Как код функции JumpFigure() узнает, объект какого класса фактически передан ей? Да никак. JumpFigure() и не знает класс этого объекта. JumpFigure() только делает ссылку на идентификаторы, определенные в классе Point. Благодаря наследованию эти идентификаторы определены так же в любом классе, порожденном классом Point.

Методы GetX(), GetY(), Show() и MoveTo() фактически присутствуют в классе Circle, так же как и в Point, и будут присутствовать в любом будущем классе, определенном в качестве потомка как Point, так и Circle.

Методы GetX(), GetY() и MoveTo() статические методы. Это означает, что JumpFigure() знает адрес каждой процедуры еще во время компиляции.

С другой стороны, Show() - это виртуальный метод. Есть различные реализации Show() для Point и для Circle, и JumpFigure() не знает во время компиляции, какая реализация будет вызываться.

Короче говоря, когда JumpFigure() вызывается, она ищет адрес правильной реализации Show() в таблице виртуальных методов класса, переданного в AnyFigure. Если экземпляр имеет тип Point, то вызовется Point.Show(), если это объект класса Circle, то Circle.Show(). Решение будет принято в момент вызова.

Итак, мы достигли значительного результата. Теперь JumpFigure() может изобразить объект любого класса, порожденного классом Point, независимо от того, существовал ли данный класс при компиляции функции JumpFigure() или нет.



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