OBJECTS (Методичка С++), страница 2
Описание файла
Файл "OBJECTS" внутри архива находится в папке "METODY". Документ из архива "Методичка С++", который расположен в категории "". Всё это находится в предмете "информатика" из 2 семестр, которые можно найти в файловом архиве МГТУ им. Н.Э.Баумана. Не смотря на прямую связь этого архива с МГТУ им. Н.Э.Баумана, его также можно найти и в других разделах. Архив можно найти в разделе "книги и методические указания", в предмете "информатика" в общих файлах.
Онлайн просмотр документа "OBJECTS"
Текст 2 страницы из документа "OBJECTS"
end;
TPerson=Object(TName) {объект-потомок}
adress:string[20];
procedure Print_All;
end;
Наследование - мощнейший аппарат ООП, позволяющий создавать библиотеки объектов по принципу от простого к сложному!
Между экземплярами родственных объектов возможен обмен информацией посредством оператора присваивания, но при этом разрешено только экземпляру объекта-родителя присваивать значения полей экземпляра объекта-потомка, так как обратное присваивание не определяет всех полей объекта-потомка и потому считается недопустимым.
Например:
Var a:TName; b:TPenson;
... a:=b; {разрешено}
b:=a; {недопустимо}
1.5. Полиморфизм.
Объект-потомок может не только дополнять свои методы к методам объекта-родителя, но и заменять методы объекта родителя на свои. При этом объект-потомок содержит объявление метода, имя которого совпадает с родительским.
Примечание. Заменить поле объекта-родителя нельзя.
Пример 5. Наследование. Полиморфизм. Раннее связывание.
{Программа печати имени, фамилии и телефона.}
Type TName=Object {объект-родитель}
family,name:string[22];
procedure Print;
end;
Procedure TName.Print; {процедура печати объекта-родителя}
Begin WriteLn(family,' ',name) end;
Type TPerson=Object(TName) {объект-потомок}
fon:string[7];
procedure Print;
end;
Procedure TPerson.Print; {процедура печати объекта-потомка}
Begin WriteLn(family,' ',name,' ',fon) end;
Const A:TName=(family:'Петров';name:'Петр');
B:TPerson=(family:'Иванов';name:'Иван';fon:'1111111');
Begin
A.Print;
B.Print;
end.
Метод, объявленный в объекте родителе вместе со всеми методами, носящими то же имя и объявленными в объектах-потомках, образуют полиморфный метод. Отдельные определения методов для объектов носят название аспектов полиморфного метода.
При необходимости объект-потомок может вызывать аспект полиморфного метода объекта-родителя, указав в качестве уточняющей информации имя типа объекта родителя. Аналогично можно вызвать и конструктор объекта-родителя.
Например, в примере, приведенном выше, метод TPerson.Print можно было бы описать так:
Procedure TPerson.Print; {процедура печати объекта-потомка}
Begin TName.Print; {обращение к методу объекта-родителя}
WriteLn(fon)
End;
1.6. Раннее и позднее связывание. Виртуальные методы. Конструкторы. Определение типа объекта.
В приведенном выше примере связь объекта с методом выполняется (как принято обычно в Borland Pascal) в процессе компиляции и называется ранним связыванием. Соответствующие методы носят название статических.
Однако использование только статических методов существенно ограничивало возможности ООП. Поэтому в язык был добавлен механизм позднего связывания, при использовании которого, связь объекта с методом выполняется на этапе выполнения программы. При этом появляется возможность передавать в процедуру или функцию вместо формально описанного объекта-родителя объект-потомок (которые таким образом образуют единый полиморфный объект). Если метод с полиморфным объектом в качестве параметра в свою очередь вызывает полиморфный метод, то нужный аспект в этом случае определяется в зависимости от типа объекта, для которого вызван исходный метод.
Методы, для которых должно применяться позднее связывание, описываются с директивой virtual и получили название виртуальных.
Поясним сказанное на таком примере.
Допустим, что у нас объект-родитель содержит метод Out, который в процессе выполнения вызывает метод Print. Объект-потомок - наследует метод Out, но имеет свой метод Print (см. Рис.1.1). Если метод Print - статический, то метод Out вне зависимости от типа объекта, для которого он вызван, будет вызывать метод Print объекта-родителя (на этапе компиляции эта связь установлена раз и навсегда).
Рис. 1.1. Пример позднего связывания.
Объявление метода Print виртуальным приводит к тому, что прежде, чем вызвать метод Print из метода Out, будет анализироваться тип экземпляра объекта, для которого вызван метод Out, и, в соответствии с результатом анализа, будет вызываться нужный аспект полиморфного метода, т.е. для потомка будет вызываться метод потомка.
Позднее связывание реализуется следующим образом. Для каждого типа объекта создается специальная таблица виртуальных методов (ТВМ), в которой хранятся адреса этих методов.
Каждый экземпляр объекта пользуется этой таблицей при вызове виртуальных методов. Объект, использующий виртуальные методы, обязательно должен включать специальный статический метод - конструктор. При вызове конструктора в специальное поле экземпляра объекта автоматически заносится адрес ТВМ.
Конструктор по форме отличается от обычных методов только тем, что вместо слова Procedure при его описании используется слово Constructor (кроме этого, принято называть его Init, но это не обязательно). До вызова конструктора нельзя использовать виртуальные методы объекта (это приводит к нарушению адресации и может потребовать перезагрузки машины). Обычно тело конструктора используется для инициализации полей объекта, но оно может и не содержать выполняемых операторов.
В качестве примера рассмотрим программу, определяющую стоимость комнаты или квартиры по заданным размерам помещений и стоимости метра площади.
Иерархия объектов для решения данной задачи приведена на Рис.1.2, а структура полей объектов на Рис.1.3.
Рис.1.2. Иерархия объектов.
Рис. 1.3. Структура полей объектов.
Пример 6. Позднее связывание. Виртуальные методы.
{ Программа определения стоимости комнаты или квартиры.}
Program flat_r;
Type
RRoom=record length, wight:real; end;
TSpace=Object {абстрактный объект - помещение}
cost:real; {стоимость метра площади}
function Square:real;virtual; {площадь помещения}
function Price:real; {стоимость квартиры}
end;
TRoom=Object(TSpace) {объект - комната}
length, wight:real; {размеры комнаты}
function Square:real;virtual; {площадь комнаты}
constructor Init(c,l,w:real);
end;
TFlat=Object(TSpace) {объект - квартира}
n:integer; {количество комнат}
rooms:array[1..10] of TRoom; {массив объектов «комната» }
function Square:real;virtual; {площадь квартиры}
constructor Init(nn:integer; c:real; Var mas);
end;
Function TSpace.Price:real; {функция определения стоимости}
Begin price:=cost*Square; End;
Function TSpace.Square:real; {функция определения площади}
Begin Square:=0; End;
Function TRoom.Square:real; {функция определения площади}
Begin Square:=length*wight; End;
Constructor TRoom.Init(c,l,w:real);
Begin cost:=c;
length:=l;
wight:=w;
End;
Function TFlat.Square:real; {функция определения площади}
Var i:integer; s:real;
Begin s:=0;
for i:=1 to n do s:=s+rooms[i].square;
square:=s;
End;
Constructor TFlat.Init(nn:integer;c:real;Var mas);
Var i:integer;
m:array[1..10] of RRoom absolute mas;
Begin n:=nn;
cost:=c;
for i:=1 to n do
rooms[i].Init(0,m[i].length,m[i].wight);
End;
Const mr:array[1..3] of RRoom=((length:3;wight:3),
(length:4;wight:4),
(length:3;wight:5));
Var A:TRoom; B:TFlat;
Begin A.Init(50,5,6);
B.Init(3,60,mr);
WriteLn(A.price);
WriteLn(B.price);
End.
В приведенном примере объекты-потомки «Комната» и «Квартира» используют метод определения стоимости объекта-родителя «Помещение», но каждый раз при определении стоимости функция определения площади вызывается для соответствующего объекта-потомка, так как эта функция является полиморфной и виртуальной.
При определении того, какой (статический или виртуальный) метод использовать при решении задачи следует иметь в виду, что:
-
виртуальные методы занимают больше памяти (из-за ТВМ);
-
вызов виртуальных методов выполняется медленнее, чем статических;
-
полиморфная функция не может содержать и статические и виртуальные аспекты одновременно;
-
списки параметров статических аспектов полиморфного метода могут различаться, а виртуальных - нет;
-
однако, при создании иерархий объектов использование виртуальных методов позволяет организовать более гибкое взаимодействие методов.
При необходимости программист может определить тип объекта, с которым в настоящий момент времени работает виртуальный метод. Для этого используется стандартная функция Паскаля TypeOf(TObj), которая для объектов возвращает адрес ТВМ. Сравнив этот адрес с адресом интересующего нас типа, мы определим тип объекта, с которым работает метод:
if TypeOf(Self)=TypeOf(TA) then ...
1.7. Динамические объекты. Деструкторы.
Объектные переменные во многом подобны обычным, в частности, их можно размещать в динамической памяти. Для выделения памяти экземпляру объекта можно использовать специальный вариант функции или процедуры New.
Процедура: New(<указатель на объект>[,<имя конструктора>]);
Функция:
<указатель на объект>:= New(<тип объекта>[,<имя конструктора>])
При запросе памяти под экземпляр объекта, не содержащего виртуальных методов, указание имени конструктора не обязательно.
Для освобождения памяти при уничтожении динамического экземпляра объекта используется процедура
Dispose(<указатель на объект>,[<имя деструктора>]);
Деструктор - это специальная процедура, при описании которой служебное слово Procedure заменяется служебным словом Destructor (так же как и для конструктора, для деструктора существует рекомендованное имя - Done). Деструктор используется для определения действий, которые необходимо выполнить перед уничтожением объекта. Если динамический объект использует виртуальные методы, то использование деструктора обязательно. Обычно деструктор определяет действия по освобождению памяти, распределенной для размещения динамических полей, как статических, так и динамических объектов, но может и не содержать выполняемых операторов.
Пример 7. Динамический объект со статическим полем без деструктора.