"Адское" программирование Ada-95 -Компилятор GNAT

         

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



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

Одним из широко распространенных случев использования объектов контролируемых типов являестя решение проблемы "утечки" памяти при работе с динамическими данными.

Предположим, что у нас есть пакет Lists который содержит описание связанного списка List, и спецификация этого пакета выглядит следующим образом:

package Lists is type List is private; Underflow : exception; procedure Insert_At_Head(Item : in out List; Value : in Integer); procedure Remove_From_Head(Item : in out List; Value : out Integer); procedure Insert_At_Tail(Item : in out List; Value : in Integer); procedure Remove_From_Tail(Item : in out List; Value : out Integer); function Full(Item : List) return Boolean; function Empty(Item : List) return Boolean;private type List is ... -- полное описание типа . . .end Lists;

Заметим, что тип связанного списка List, который описан в этом пакете, не является контролируемым типом.Теперь, рассмотрим следующий пример, использующий описание пакета Lists:



with Lists; use Lists;procedure Memory_Leaks_Demo is A, B : List; Result : Integer; procedure Loose_Memory is C : List; begin Insert_At_Head(C, 1); Insert_At_Head(C, 2); end Loose_Memory; -- при попадании в эту точку, -- C выходит за пределы области видимости -- и теряется память ассоциируемая с этим узломbegin Insert_At_Head(A, 1); Insert_At_Head(A, 2); Insert_At_Head(B, 3); Insert_At_Head(B, 4); B := A; -- B и A указывают на один и тот же список -- все узлы "старого" списка B - теряются Remove_From_Tail(A, Result); -- изменяет как список A, так и список Bend Memory_Leaks_Demo;

В данном примере наглядно демонстрируются проблемы "утечки" памяти при работе с объектами связанного списка List:

  • когда объект связанного списка выходит за пределы области видимости, пространство динамической памяти, выделенное для размещения этого объекта, не восстанавливается
  • в случае выполнения присваивания, осуществляется копирование только указателя на "голову" списка (начало списка), а копирование остальной части списка не выполняется

Рассмотрим вариант модификации пакета Lists в котором тип связанного списка List является контролируемым. Спецификация пакета будет иметь следующий вид:

with Ada.Finalization;package Lists is type List is private; Underflow : exception; procedure Insert_At_Head(Item : in out List; Value : in Integer); procedure Remove_From_Head(Item : in out List; Value : out Integer); procedure Insert_At_Tail(Item : in out List; Value : in Integer); procedure Remove_From_Tail(Item : in out List; Value : out Integer); function Full(Item :List) return Boolean; function Empty(Item:List) return Boolean;private -- обычные описания для списка type Node; type Ptr is access Node; type Node is record Value : Integer; Next : Ptr; end record; -- только "голова" списка - "специальная" type List is new Ada.Finalization.Controlled with record Head : Ptr; end record; -- Initialize не нужна (указатели автоматически устанавливаются в null) -- procedure Initialize(Item : in out List); procedure Adjust(Item : in out List); procedure Finalize(Item : in out List);end Lists;

Примечательным фактом является описание подпрограмм Adjust и Finalize в приватной части спецификации пакета.Это предотвращает от непосредственной возможности их неуместного вызова клиентами.Тело пакета Lists (с подробными комментариями) будет иметь вид:

with Unchecked_Deallocation;package body Lists is -- подпрограмма освобождения памяти занимаемой узлом procedure Free is new Unchecked_Deallocation(Node, Ptr); ------------------------------------------------------------ -- реализация остальных внутренностей (Insert_At_Head, Remove_From_Head...) . . . -------------------------------------------------------------- -- дан указатель на список, подпрограмма будет размещать -- в памяти новый, идентичный первому, список function Copy_List(Item : Ptr) return Ptr is begin if Item = null then return null; else return new Node'(Item.Value, Copy_List(Item.Next)); end if; end Copy_List; ------------------------------------------------------------ -- при присваивании B := A, B будет только переписано содержимым A. -- для связанного списка это подразумевает, что оба, A и B, -- указывают на один и тот же объект. -- теперь, необходимо сделать физическую копию узлов, на которые указывает B, -- и переставить указатель начала списка на начало копии списка, который мы -- только что сделали procedure Adjust(Item : in out List) is begin Item.Head := Copy_List(Item.Head); end Adjust; ------------------------------------------------------------ -- освободить всю память, занимаемую узлами списка, -- при разрушении списка procedure Finalize(Item : in out List) is Upto : Ptr := Item.Head; Temp : Ptr; begin while Upto /= null loop Temp := Upto; Upto := Upto.Next; Free(Temp); end loop; Item.Head := null; end Finalize;end Lists;

Ниже представлен пример программы которая использует модифицированную версию пакета Lists (где тип List является контролируемым).

with Lists; use Lists;procedure Controlled_Demo is A : List; -- автоматический вызов Initialize(A); B : List; -- автоматический вызов Initialize(B);begin Insert_At_Head(A, 1); Insert_At_Head(A, 2); Insert_At_Head(B, 3); Insert_At_Head(B, 4); ------------------------------------------ -- -- A --> | 2 |-->| 1 |--> null -- B --> | 4 |-->| 3 |--> null -- ------------------------------------------ B := A; ------------------------------------------ -- -- Finalize(B); -- освобождение узлов B, до перезаписи -- -- A --> | 2 |-->| 1 |--> null -- B --> null -- -- копирование A в B -- теперь они _оба_ указывают на один и тот же список -- -- A --> | 2 |-->| 1 |--> null -- B ----^ -- -- Adjust(B); -- B копирует список, на который он в текущий момент -- указывает, и после этого указывает на новый список -- -- A --> | 2 |-->| 1 |--> null -- B --> | 2 |-->| 1 |--> null -- ------------------------------------------end Controlled_Demo; ------------------------------------------ -- -- Finalize(A), Finalize(B). -- освобождение памяти ассоциируемой с A и B -- -- A --> null -- B --> null -- ------------------------------------------

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



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








Forekc.ru
Рефераты, дипломы, курсовые, выпускные и квалификационные работы, диссертации, учебники, учебные пособия, лекции, методические пособия и рекомендации, программы и курсы обучения, публикации из профильных изданий