4. Typen und Datenstrukturen
 
zurück

4.5 Überwachte Typen ("Controlled Types")


Ein sehr nützlicher Bestandteil von Ada 95 sind die überwachten Typen. Dieses Sprachkonstrukt macht unter anderem eine sehr elegante Implementierung der Speicherbereinigung möglich.

Die überwachten Typen ermöglichen eine kontrollierte Verwaltung von Speicherressourcen, indem geeignete Operationen dafür zur Verfügung gestellt werden. Das allgemeine Prinzip basiert auf drei verschiedenen primitiven Operationen für die Behandlung von Objekten:

  • "Initialize" zur Initialisierung nach der Erzeugung eines Objektes,
  • "Finalize" zum definierten Beenden vor dem Löschen des Objektes (auch bei Kopieraktionen),
  • "Adjust" zur Anpassung nach der Zuweisung, z. B. beim Kopieren eines Objektes.
Bei der Deklaration von Objekten eines überwachten Typen wird für jedes Objekt die Prozedur INITIALIZE automatisch aufgerufen. Bei Zuweisungen mittels ":=" wird automatisch die Prozedur ADJUST aufgerufen, und, wenn das Ende des Geltungsbereiches eines Objektes erreicht wird, wird automatisch die Prozedur FINALIZE aufgerufen. Diese letzte Prozedur wird selbst dann ausgeführt, wenn der Geltungsbereich durch das Auftreten einer Ausnahme verlassen wird!

Beispiel:

declare
   A: T; -- A erzeugen, Initialize(A)
   B: T; := Initialisierungs_Ausdruck; -- B erzeugen, Adjust(B)
begin
   A := E ; -- Finalize(A), Wert kopieren, Adjust(A)
   ...
end; -- Finalize(A), Finalize(B)

Der Nutzer kann die vordefinierten Prozeduren benutzen oder geeignet überladen. Diese Prozeduren heißen "Initialize", "Finalize" und "Adjust" und haben das Objekt des überwachten Typs als Parameter mit dem Modus "in out". Sie werden automatisch an verschiedenen Stellen im Programm aufgerufen.

Das Beispiel zeigt den automatischen Aufruf der Prozeduren an verschiedenen Stellen eines Programmfragments. "T" sei dabei ein überwachter Typ.

Nachdem "A" deklariert worden ist und jede normale Initialisierung ausgeführt wurde, wird die Prozedur "Initialize" aufgerufen. Bei einer Deklaration mit expliziter Vorbesetzung wie bei "B" wird dagegen "Adjust" aufgerufen.

Bei der Zuweisung wird zuerst "Finalize" aufgerufen, um das alte Objekt zu beenden, damit es überschrieben werden kann, dann wird die Kopie angefertigt und schließlich wird "Adjust" aufgerufen um alles auszuführen, was für die korrekte Besetzung der neuen Kopie nötig ist.

Am Ende des Blockes wird "Finalize" noch einmal aufgerufen, bevor die Objekte beendet werden. Zu beachten ist, daß der Nutzer die Aufrufe an die drei Prozeduren nicht explizit zu schreiben braucht. Die Aufrufe werden automatisch beim Übersetzen erzeugt.

Im Falle verschachtelter Strukturen bei denen innere Komponenten "controlled" sind, besagen die Regeln, daß zunächst diese inneren Komponenten initialisiert und angepaßt werden, bevor das Objekt als Ganzes behandelt wird. Bei der Beendigung läuft alles in umgekehrter Reihenfolge ab.

Um einen überwachten Typ zu verwenden, muß er von einem von zwei erweiterbaren Typen aus dem Paket "Ada.Finalization" abgeleitet werden. Die Spezifikation ist:

package Ada.Finalization is

   type Controlled is abstract tagged private;

   procedure Initialize(Object: in out Controlled);
   procedure Adjust (Object: in out Controlled);
   procedure Finalize (Object: in out Controlled);

   type Limited_Controlled is abstract tagged limited private;

   procedure Initialize(Object: in out Limited_Controlled);
   procedure Finalize (Object: in out Limited_Controlled);

private
   ...
end Ada.Finalization;


Es werden zwei unterschiedliche abstrakte Typen für nicht limitierte und für limitierte Typen angeboten. Für limitierte Typen existiert keine Prozedur "Adjust", da Zuweisungen für limitierte Typen nicht definiert sind.

Obwohl die Typen "Controlled" und "Limited_Controlled" abstrakt sind, sind die Prozeduren "Initialize", "Adjust" und "Finalize" nicht abstrakt. Sie haben alle einen leeren Anweisungsteil (begin null; end;), tun also nichts, was in vielen Fällen genau das ist, was der Nutzer möchte. Er braucht dann diese Prozeduren für seine von "Controlled" oder "Limited_Controlled" abgeleiteten Typen nicht zu überschreiben.

Eine typische Deklaration eines überwachten Typs sieht so aus:

type T is new Ada.Finalization.Controlled with record...

Der Anwender muß dann neue Versionen für die Prozeduren schreiben, die er benötigt (also nicht unbedingt für alle drei).

Die Möglichkeiten der überwachten Typen bieten dem Programmierer die Kontrolle über Speicherressourcen. Im besonderen erlauben sie die Garantie eines sauberen Aufräumens, bevor ein Objekt für immer unzugreifbar wird. Die Möglichkeit einer automatischen Beendigung ist extrem wichtig, besonders wenn man berücksichtigt, daß ein Gültigkeitsbereich auf viele verschiedene Arten verlassen werden kann (Ausnahme, Exit-Anweisung, Return-Anweisung, Abort-Anweisung, asynchroner Prozesswechsel etc). In all diesen Fällen wird automatisch "Finalize" für alle in dem Gültigkeitsbereich deklarierten Objekte eines überwachten Typs aufgerufen.

In vielen Fällen werden überwachte Typen mehr aus Gründen der Sicherheit und Zuverlässigkeit eingesetzt und sind nicht unbedingt sichtbarer Teil der Spezifikation. Die meisten Benutzer einer Abstraktion brauchen nicht zu wissen, ob die Implementierung überwachte Typen benutzt oder nicht.

In diesem Beispiel wird z. B. anhand der Nummer die Reihenfolge der Erstellung von zwei Objekten ermittelt. obmieinu.ads

with Ada.Finalization;
use Ada.Finalization;

package Objekt_Mit_Eindeutiger_Nummer is

   -- erweitere "Controlled" um eigene Elemente :
   type Mein_Objekt is new Limited_Controlled with record
      Objekt_Nummer : Integer;
      Daten : Integer;
   end record;

   -- und füge eigene Prozeduren hinzu:
   -- Die Prozedur Initialize wird hier überladen:
   procedure Initialize (X : IN OUT Mein_Objekt);

   -- Die Prozedur Finalize wird hier überladen:
   procedure Finalize (X : IN OUT Mein_Objekt);

end Objekt_Mit_Eindeutiger_Nummer;

with Ada.Finalization, Text_IO;
use Ada.Finalization, Text_IO;

package body Objekt_Mit_Eindeutiger_Nummer is

   Zähler : Integer := 0; -- lokaler Zähler

   procedure Initialize (X : IN OUT Mein_Objekt) is
   begin
      Zähler := Zähler + 1;
      X.Objekt_Nummer := Zähler;
      X.Daten := 4711;
      Put_Line ("Initialisierung!");
   end;

   procedure Finalize (X : IN OUT Mein_Objekt) is
   begin
      -- benutze die Objekt-Nummer für irgendwelche
      -- letzten Aktionen bezüglich des Objektes ...
      Put_Line ("Freigabe!");
   end;

end Objekt_Mit_Eindeutiger_Nummer;

with Objekt_mit_eindeutiger_Nummer, Text_IO;
use Objekt_mit_eindeutiger_Nummer, Text_IO;

procedure Ueberwachte_Typen is
   -- die Prozedur Initialize wird aufgerufen und
   -- der interne Zähler wird auf 1 gesetzt:
   Objekt1 : Mein_Objekt;

   -- die Prozedur Initialize wird aufgerufen und
   -- der interne Zähler wird auf 2 gesetzt:
   Objekt2 : Mein_Objekt;

begin
   -- Verwendung von "Objekt:"
   if Objekt1.Objekt_Nummer < Objekt2.Objekt_Nummer then
      Put_Line ("Obj1 wurde vor Obj2 angelegt!");
   end if;

   -- die Objekte "Objekt1" und "Objekt2" werden
   -- bei Verlassen des Gültigkeitsbereiches unter
   -- Verwendung der Prozedur "Finalize" wieder freigegeben:
end;




 
zurück
 Index   Ada Tour - Dokumentation  
© 2003 Förderverein Ada Deutschland e.V.