Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Динамические полиморфные объекты. Деструкторы






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

Функция New(< тип указателя> [, < вызов конструктора> ]) - возвращает адрес размещенного и, возможно, сконструированного объекта. Квадратные скобки означают, что второй параметр может быть опущен.

При уничтожении полиморфных объектов необходимо учитывать наличие у него дополнительного поля, содержащего адрес ТВМ. Для корректного освобождения памяти следует предварительно вызвать специальный метод класса - деструктор. Деструктором может служить любой, в том числе и пустой метод класса, при описании которого служебное слово procedure заменено служебным еловом destructor. Если деструктор переопределяется, то его следует объявить виртуальным полиморфным, чтобы его вызов также выполнялся через ТБМ.

Обычно деструктору присваивается имя Done. Он может быть инициирован отдельным оператором или для его активизации можно использовать специальную форму процедуры освобождения памяти Dispose.

Процедура Dispose (< указатель> {, < вызов деструктора]) - выполняет вызов деструктора, если он указан, и освобождает память.

Пример. Разработать классы для реализации объекта «Комната Д2», который должен отвечать на запрос о площади пола, и объекта «Трехмерная комната Д2», который должен отвечать на запрос о площади стен и потолка. Предусмотреть возможность создания динамических объектов разработанных классов и хранения адреса объекта производного класса в поле указателя базового класса (2-й случай обязательного использования сложного полиморфизма).

Описание классов отличается от выполненных в программе предыдущего примера тем, что для обоих классов объявляется деструктор. Его придется объявить визуальным полиморфным, так как возможен доступ через указатель базового класса к деструктору производного класса.

Program ex;

Type

pTRoomD2=^TRoomD2;

TRoomD2=object

length, width: real; {поля: длина и ширина комнаты}

function Square: real; virtual; {метод определения площади}

constructor Init(l, w: real); {конструктор}

destructor Done; {деструктор}

end;

Function TRoomD2.Square; {тело метода определения площади}

Begin

Square: =length*width;

End;

Constructor TRoomD2.Init; {тело конструктора}

Begin

length: =l; width: =w:

End;

Destructor TRoomD2.Done;

Begin

End;

Type pTVRoomD2=^TVRoomD2;

TRoomD2 = object(TRoomD2)

Height: real; {дополнительное поле класса}

ftmction Square: real; virtual; {виртуальный полиморфный метод}

constructor Init(i, w, h: real); {конструктор}

end;

Constructor TVRoomD2.Init;

Begin

inherited Init(l, w), {инициализирует поля базового класса}

height: =h; {инициализируем собственное поле класса}

End;

Function TVRoomD2. Square;

Begin

Square: =inherited Square*height+2*height*(length + width);

End;

Var pA: pTRoomD2;

pB: pTVRoomD2; {объявляем указатели}

Begin {объект базового класса - указатель базового класса}

pA: =New(pTRoomD2.Init(3.5, 5.1)); {конструируем объект}

WriteLn('Площадь=', pA^.Square: 6: 2); {выведет «Плшцаль= 17.85»}

Dispose(pA.Done); {уничтожаем объект}

{объект производного класса - указатель производного класса}

pB: =New(pTVRoomD2.Init(3.5, 5.1, 2.7)); {конструируем объект}

WriteLп('Площадь= ', pB^.Square: 6: 2); {выведет «Площадь- 94, 64»}

Dispoze(pB.Done); {уничтожаем объект}

{проявление полиморфных свойств: объект произвольного класса - указатель базового класса}

pA: =New(pTVRoomD2.Init(3.5, 5.1, 2.7)); {конструируем объект}

WriteLn('Площадь ='. pA^.Square: 6: 2); {выведет «Плпщадь= 94.64»}

Dispose(pA.Done); {уничтожаем объекг}

End.

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

Возможна ситуация, когда в конструкторе запрашивается память под размещение нескольких динамических полей, но реально удовлетворяется только часть из них, например, только запрос памяти под размещение самого объекта. Естественно такой обьекг в программе использоваться не может. В Borland Pascal существует специальный оператор fail, при выполнении которого все ранее удовлетворенные запросы на память аннулируются. При этом, если объект динамический, то указателю присваивается значение nil, a если объект статический, то конструктор, несмотря на то, что формально он является процедурой, возвращает значение false.

Пример.Разработать класс для реализации объекта «Комната с балконом Д», который должен реагировать на запрос об общей площади пола и о площади балкона. Предусмотреть возможность создания динамических и статических объектов, возможность сохранения адреса объекта-потомка в указателе типа объекта-родителя и контроль выделения памяти.

Для реализации объекта используем иерархию с наполнением. В качестве базового используем класс TRoomD3, аналогичный TRoomD2 разработанному в предыдущем примере. Единственное отличие этого класса заключается в том, что его деструктор объявлен виртуальным, так как он переопределяется классом TBRoomD, который использует деструктор для освобождения памяти, выделенной под динамическое поле.

Program ex;

Type pTRoomD3=^TRoomD3;

TRoomD3=object

length, width: real; {поля: длина и ширина комнаты}

function Square: real; virtual; {метод определения площади}

constructor Init(l, w: real); {конструктор}

destructor Done; virtual; {деструктор}

end;

Function TRoomD3.Square; {метод определения площади}

Begin

Square: =length*width;

End;

Constructor TRoomD3.Init; {конструктор}

Begin

length: =l;

width: =w;

End;

Destructor TRoomD3.Done;

Begin

End;

Type pTBRoomD=^TBRoomD;

TBRoomD = object(TRoomD3)

pB.pTRoomD3;

function Square: real; virtual;

function BSquare: real; {площадь балкона}

constructor Init(l, w: real; lb, wb: real);

destructor Done; virtual;

end;

Constructor TBRoomD.Init;

Begin

inherited Init(l, w);

if (lb=0)or(wb=0) then pB: =nil

else

begin

New(pB);

if pB=nil then

begin

WriteLn('He хватает памяти для размещения поля');

Done; {завершение обработки; не нужно, если такой

обработки нет и деструктор пустой}

fail; {отменяет все выполненные заказы на память}

end

else pB^.Init(lb, wb);

end;

End;

Function TBRoomD.BSquare;

Begin

If pB=nil then BSquare: =pB^.Square

else BSquare: =0;

End;

Function TBRoomD.Square;

Begin

Square: = inherited Square+BSquare;

End;

Destructor TBRoomD.Done;

Begin {освобождаем память, выделенную под динамическое поле}

If pB< > nil then Dispose(pB);

End;

Function HeapFunc(size: Word): integer; far;

Begin HeapFunc: =1; end;

Var A: TBRoomD; pB1: pTBRoomD; pB2: pTRoomD3;

Begin {берем на себя обработку ошибок выделения памяти}

HeapError: =@HeapFunc:

{статический объект с динамическим полем}

if A.Init(3.2, 5.1, 2.5, 1) then {конструктор возвращает true или false}

begin

Writein(A.Square: 6: 2, A.BSquare: 6: 2); {выводим площади}

A.Done; {вызываем деструктор как обычную процедуру}

end

else WriteLn('He хватает памяти для размещения объекта.');

{динамический объект с динамическим полем –

указатель типа класса-потомка}

pB1: =New(pTBRoomD, Init(3.2, 5.1, 2.5, 1)); {new вернет адрес или nil}

if рВ1< > nil then

begin

WriteLn(pB1^.Square: 6: 2, pB1^.BSquare: 6: 2); {выводим площади}

Dispose(pB1.Done); {уничтожаем объект}

end

else writeln('Нe хватает памяти для размещения объекта.');

{динамический объект с динамическим полем –

указатель типа класса-родителя }

рВ2: =new(pTBRoomD, Init(3.2, 5.1, 2.5, 1)); {new вернет адрес или nil}

if pB2< > nil then

begin

WritcLn(pB2^.Square: 6: 2, {необходимо позднее связывание -случай 2}

pTBRoomD(pB2)^.BSquare: 6: 2); {явное переопределение типа указателя, иначе метод класса-потомка для указателя типа класса-родителя не «виден»}

Dispose(pB2, Done); {позднee связывание - случай 2}

end

else WriteLn('He хватает памяти для размещения объекта.');

End.

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.