Файл: 9. Полиморфизм на уровне объектных переменных.pdf

Добавлен: 20.10.2018

Просмотров: 835

Скачиваний: 15

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

 

 

9. Полиморфизм на уровне объектных переменных 

Как известно, объектные переменные являются скрытыми указателями 

(ссылками)  на  размещаемые  в  памяти  объекты.  Классический  механизм 

указателей  в  обычных  языках  предполагает  строгое  соответствие  типов 

указателей  и  адресуемых  данных:  переменные-указатели  могут  адресовать 

только данные тех типов, с которыми они были связаны при объявлении. 

В объектных языках это правило строгого соответствия типов немного 

ослаблено:  при  определенных  условиях  объектные  переменные  могут 

адресовать (ссылаться на) не только свои «родные» объекты, но и объекты 

некоторых других классов. Более конкретно:

 

 

Пусть объектная переменная связана при объявлении с некоторым 

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

объектам этого класса, но и к объектам любых классов, производных от 

данного.

 

 

Пусть,  например,  для  иерархии  графических  фигур  объявлены 

следующие объектные переменные: 

Fig : TFigure;  // хоть класс фигур и является абстрактным, 

 // разрешается объявлять переменную такого классового типа,  

// нельзя лишь использовать ее вместе с конструктором 

Circ : TCircle;  // доступ к объектам-окружностям 

Ellipse : TEllipse;  //  доступ к объектам-эллипсам 

Rect : TRect;  //  доступ к объектам-прямоугольникам 

 

Пусть  также  с  помощью  конструкторов  созданы  соответствующие 

объекты (окружность, эллипс, прямоугольник).  

Тогда разрешается делать следующие присваивания: 

Fig := Circ;   // переменная  Fig  адресует окружность 

Fig := Ellipse; // переменная  Fig  адресует эллипс 


background image

 

 

Fig := Rect;   // переменная  Fig  адресует прямоугольник 

Circ := Ellipse; // переменная  Circ   адресует эллипс 

 

Однако недопустимыми будут следующие присваивания: 

Circ := Fig;  Rect := Fig;  Circ := Rect;  Ellipse := Circ; 

Отсюда  следует,  что  механизм  полиморфности  указателей  работает 

только  в  рамках  единой  иерархии  классов  и  только  вниз  по  иерархии  от 

заявленного базового класса. 

А  что  будет,  если  объявить  переменную  классового  типа 

Object/TObject? Учитывая особое положение этого класса как корневого для 

всей  иерархии  стандартных  и  нестандартных  классов,  получим,  что  такая 

переменная сможет адресовать объекты абсолютно любых классов! 

Очень  мощным  использованием  полиморфных  объектных  переменных 

является возможность объединения их в единую структуру. Это позволяет 

собирать  в  один  массив  или  список  и  циклически  обрабатывать  объекты 

разных классов.  

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

объекты в рамках единой иерархии графических фигур с корневым классом 

TFigure/Figure: 

var    Figs : array [1..N]  of  TFigure; 

Figure [ ]   Figs = new  Figure [ N ]; 

Пример заполнения массива ссылками на конкретные фигуры: 

Figs[1] := TCircle.Create(…); 

Figs[2] := TRect.Create(…);   

. . . . . . . . . . . . . . . . . . . . 

Figs[0] = new  Circle(…); 

Figs[1] = new  Rect(…); 

. . . . . . . . . . . . . . . . . . . . 

Схематичное представление такого массива :

 


background image

 

 

 

   

 

 

 

 

 

 

Подобный  массив  очень  удобно  можно  обрабатывать  с  помощью 

циклов. Например — показать все фигуры: 

for  i := 1  to  count  do  Figs[ I ].Show;   

for (int  i=0; i < count;  i++) { Figs[ i ].Show( ); } 

Интересно, что в этих примерах используются оба проявления принципа 

полиморфизма  –  как  полиморфность  объектных  переменных,  так  и 

виртуальные  переопределяемые  методы:  цикл  идет  по  массиву,  для 

каждого  элемента происходит  обращение  к соответствующему  объекту,  для 

которого  с  помощью  его  таблицы  виртуальных  методов  определяется 

необходимая версия виртуального метода Show. 

Крайним случаем массива полиморфных указателей является массив, в 

который  можно  собрать  указатели  на  объекты  абсолютно  любых  классов. 

Для этого достаточно объявить массив с типом Object/TObject.  На практике 

применение  таких  ―сверхуниверсальных‖  массивов  должно  быть 

оправданным и обоснованным.  

Другим  важным  применением  полиморфных  объектных  переменных 

является  использование  их  в  качестве  параметров  методов.  Например, 

объявим метод с формальным параметром классового типа фигур: 

procedure  SomeMet (aFigs : TFigure); 

void   SomeMet (Figure   aFigs); 

В  качестве  фактического  значения  при  вызове  этого  метода  можно 

передавать указатель на объект любого дочернего графического класса: 

SomeMet (Circle);  // выполнение кода метода для окружности    

SomeMet (Rect); // выполнение кода метода для прямоугольника 

объект- 
окружность 

объект- 
эллипс 

объект- 
отрезок 

объект- 
прямоугольник 

объект- 
отрезок 

адрес 

объекта 1 

адрес 

объекта 2 

адрес 

объекта 3 

адрес 

объекта 4 

адрес 

объекта 5 

 


background image

 

 

Аналогично  ''сверхуниверсальном''  массиву  можно  объявить  метод  со 

―сверхуниверсальным‖ параметром: 

procedure  SuperMet (aObj : TObject); 

void   SuperMet (Object   aObj); 

Такой метод может принимать в качестве фактического значения ссылку на 

объект любого класса! 

К сожалению, при использовании полиморфных объектных переменных 

часто  возникает  ряд  проблем.  Поскольку  полиморфный  указатель  может 

динамически менять свой тип, возникает необходимость ответа на вопрос: 

Объект какого класса адресует полиморфная объектная 

переменная в  данный момент работы приложения?

 

 

Например,  для  массива  указателей  на  графические  фигуры  при 

использовании  конструкций  типа    Figs[i]    возникает  неопределенность

какой графический объект адресуется этим элементом? 

Для  ответа  на  подобные  вопросы  используются  специальные 

механизмы,  которые  для  каждого  объекта  в  каждый  момент  времени 

работы  программы  позволяют  получать  информацию  о  его  текущем 

классе.  Эта  информация  оформляется  в  виде  таблиц  и  хранится  в 

оперативной  памяти  в  специальном  внутреннем  формате.  Подобные 

таблицы часто называют таблицами метаданных. Метаданные – это данные 

о  данных,  т.е.  данные,  которые  описывают  структуру  других  данных,  в 

нашем случае – структуру классов. 

Несмотря  на  то,  что  конкретный  состав  метаданных  различен  для 

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

 

имя класса 

 

указатель  на  таблицу  метаданных  родительского  класса  (тем 

самым, переходя последовательно к родительским классам, можно 

для любого класса восстановить всю цепочку наследования) 

 

байтовый размер объекта 


background image

 

 

 

список всех полей данных с их атрибутами 

 

список  всех  методов  с  их  атрибутами  (открытый,  защищенный, 

закрытый, абстрактный, виртуальный и т.д.) 

Для доступа к метаданным можно использовать специальные методы, а 

сам  процесс  получения  этой  информации  во  время  работы  программы 

обозначают термином reflection (рефлексияотражение).  

Все  объекты  одного  и  того  же  класса  связаны  со  своей  таблицей 

метаданных,  для  чего  в  состав  каждого  объекта  включается  скрытое 

адресное поле.  

Например,  для  массива  указателей  на  графические  фигуры  связь 

соответствующих  структур  (сам  массив,  адресуемые  объекты  и 

соответствующие  таблицы  метаданных)  можно  схематично  представить 

следующим образом: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Из этой схемы видно, что ни элемент массива указателей, ни сам объект 

не  определяют  классовый  тип  данных,  этот  тип  определяется  таблицей 

адрес 

объекта 1 

адрес 

объекта 2 

адрес 

объекта 3 

адрес 

объекта 4 

адрес 

объекта 5 

 

Массив 
полиморфных 
указателей 

50, 50, 
100 

100, 200, 
200, 50 

50, 50, 
200, 100 

50, 50, 
200, 100 

100, 200, 
50, 0, 90 

 метаданные   
для класса    
окружностей 

 

метаданные 
для класса 
эллипсов 

 

метаданные для 
класса 
прямоугольников 

 

метаданные  
для класса дуг 
 

 

метаданные 
для класса 
фигур