4. Typen und Datenstrukturen
 
zurück
4.3.3 Zugriffstypen


Es gibt drei Arten von Zugriffstypen:
  • typisierte Zugriffstypen (Access Types), die Zugriff auf allgemeine Objekte zulassen,
  • Zugriffstypen auf Unterprogramme und
  • Zugriffstypen, die auch auf Objekte verweisen können, die nicht über die Allokatoren typisierter Zugriffstypen erzeugt werden.
Bei der Deklaration von Zugriffsobjekten werden diese implizit mit "null" vorbesetzt.
So sind die beiden Deklarationen "z1" und "z2" äquivalent, da die Werte von "z1" und "z2" beide "null" sind. z1 : access_type := null;
Die explizite Zuweisung des Literals "null" ist also unnötig. z2 : access_type;


Typisierte Zugriffstypen

Im Prinzip können für beliebige Objekttypen entsprechende Zugriffstypen eingeführt werden. Der wohl wichtigste Anwendungsbereich erschließt sich durch Zugriffe auf Verbundtypen, wie sie in dynamischen Datenstrukturen vorkommen.

Ein Beispiel für eine Zugriffstypdeklaration (access_type_definition <BNF>) ist:

package Complex is
   type Cartesian is record
      re, im: Float;
   end record;

   type Cartesian_P is access Cartesian;
   ...
end Complex;



Deklaration eines Zeigerobjektes

Die Deklaration eines Zeigerobjektes sieht wie gewohnt aus. Der Wert des Objekts wird implizit mit "null" initialisiert.

z : Complex.Cartesian_P;
x : Complex.Cartesian;
r : Float;



Zugriff auf ein Objekt

Ein Zugriff auf den "Inhalt" von "z" erfolgt durch

r := z.re;
x := z.all;


löst im gegenwärtigen Zustand einen Constraint_Error aus, da der Speicher, auf den z verweisen soll, noch nicht alloziert wurde.
Die Allozierung geschieht mit dem Allokator "new" gemäß z := new Complex.Cartesian;

Jetzt können die obigen Zugriffe auf "z" durchgeführt werden. Allerdings sollte der Inhalt von "z" noch initialisiert werden.

Initialisierung

z.re := 1.0;
z.im := 0.0; oder z.all := Complex.Cartesian'(re => 1.0, im => 0.0);


Initialisierung gleich bei der Allozierung

z := new Complex.Cartesian'(re => 1.0, im => 0.0);


Initialisierung direkt bei der Deklaration des Zugriffsobjekts

z : Complex.Cartesian_P := new Complex.Cartesian'(re => 1.0, im => 0.0);


Wenn die Komponenten "re" und "im" vorbesetzt wären, würde die reine Allozierung ausreichen, um das referenzierte Objekt z mit dieser Defaultinitialisierung zu besetzen.


Zugriffstypen auf Unterprogramme

In Ada können Zugriffstypen auf Unterprogramme (access_to_subprogram_definition <BNF>) definiert werden. Diese können dann als Komponenten in strukturierte Typen aufgenommen oder direkt als Parameter an Unterprogramme übergeben werden. Mit dieser Sprachkonstruktion lassen sich viele Probleme der Numerik elegant lösen. Die für grafische Benutzungsoberflächen übliche Programmierung mit sogenannten Call-back-Routinen ist damit ebenfalls möglich. Über diese Call-back-Routinen werden Funktionalitäten angebunden, die aufgrund bestimmter Benutzeraktionen ausgeführt werden.

Ein einfaches Beispiel ist die numerische Integration. Hier hat man einen allgemeinen Integrationsalgorithmus, dem man die zu integrierende Funktion als Parameter übergeben kann.
In der einfachsten Form kann die Konstruktion so aussehen:

type Integrand is access function (x : in Float_64) return Float_64;

function Integral(Funktion : in Integrand;
                  Untergrenze,
                  Obergrenze : in Float_64;
                  Intervalle : in positive := 1000) return Float_64;


Das Integral z. B. von exp(x) zwischen bestimmten Grenzen kann man dann so berechnen:

Fläche: Float_64 := Integral(Funktion    => exp'access,
                             Untergrenze => -1.0,
                             Obergrenze  => 1.0);



Die Funktion wird also mit dem 'Access-Attribut übergeben. In der Funktion "Integral" wird "Funktion" mit der Qualifikation ".all" benutzt gemäß:

f := Funktion.all(x);


Allgemeine Zugriffstypen

In Ada können sogenannte allgemeine Zugriffstypen (access_to_object_definition <BNF>) deklariert werden. Damit ist ein Zugriff nicht nur auf Objekte eines mit "new" allozierten Objekts möglich.

Deklaration eines allgemeinen Zugriffstyps

type Integer_Access is access all Integer;


Das Schlüsselwort "all" unterscheidet diesen allgemeinen Zugriffstyp von einem typisierten der Art

type Integer_Ptr is access Integer;


Ganzzahlige Werte, auf die mit Objekten vom Typ Integer_Access zugegriffen werden soll, müssen als "aliased" (object_declaration <BNF>) gekennzeichnet werden:

   ...
   IP : Integer_Access;
   I  : aliased Integer := 10;
begin
   Put_line("I = " & Integer'image(i));
   IP := I'access;
   IP.all := 20; -- I = 20
   Put_line("I = " & Integer'image(i));
   ...



Bei einer Typdeklaration kann durch das Schlüsselwort "constant" angezeigt werden, daß über die Objekte dieser allgemeinen Zugriffstypen nur ein lesender Zugriff auf Objekte möglich ist.

type Constant_Integer_Access is access constant Integer;


Die Benutzung sieht so aus:

   ...
   CIP : Constant_Integer_Access;
   CI  : aliased Integer := 100;
begin
   Put_line("CI = " & Integer'image(ci));
   CIP := CI'access;
   CIP.all := 20; -- verboten
   ...


Das Schlüsselwort "Constant" in der Zugriffstypdeklaration bezieht sich also nicht auf die mit "aliased" gekennzeichneten referenzierten Objekte (die nicht konstant sein müssen), sondern auf die Benutzung dieser Objekte über Objekte des allgemeinen Zugriffstyps. Die letzte Zeile ist daher genauso fehlerhaft wie eine Zuweisung an eine Konstante. ine Zuweisung an eine Konstante.

Ein sinnvoller Einsatz von Zugriffstypen ist im Zusammenhang mit strukturierten Typen, uneingeschränkten Typen
und rekursiven Datenstrukturen gegeben.

Ein sinnvoller Einsatz von Zugriffstypen ist im Zusammenhang mit strukturierten Typen gegeben. Die Benutzung von Komponenten dieser Typen erfolgt dann genauso wie bei Zugriffen auf Komponenten von Objekten des referenzierten Typs. Das Schlüsselwort "all" kann verwendet werden.

x.re := z.re;
x.im := z.all.im;
-- Soll der strukturierte Typ als Ganzes benutzt werden,
-- ist aber das Schlüsselwort "all" nötig.
...
function "+"(x,y : complex.cartesian) return complex.cartesian;
...
z.all := z.all + x;



Zugriff auf uneingeschränkte Typen

Sollen strukturierte Objekte Komponenten uneingeschränkter Typen haben, so gibt es zwei Möglichkeiten. Entweder werden die Diskriminanten vom äußeren Objekt an die inneren Komponenten übergeben oder die Komponenten sind von einem Zugriffstyp.

Die relevanten Daten der ganzen Belegschaft eines Betriebes könnten so gespeichert werden:

type Personen_daten_p is access Personen_daten;
type Personen_daten_Feld is array(Positive range <>) of Personen_daten_p;

Mitarbeiter : Personen_daten_Feld(1 .. 10_000);
Anzahl_Mitarbeiter: natural := 0;

Alle 10000 Komponenten des Feldes Mitarbeiter sind mit null initialisiert.


Zugriff bei rekursiven Datenstrukturen

Das wichtigste Einsatzgebiet für Zugriffstypen sind rekursive Datenstrukturen. Einfach verkettete Listen werden nach folgendem Schema vereinbart:

with text_io;
Procedure testlist is

   type Zelle;
   type Liste is access Zelle;

   type Zelle is record
      Inhalt : string(1 .. 32) := (others => ' ');
      Nachfolger: Liste;
   end record;

   Temp_Liste, Namens_Liste : Liste;
begin
   Temp_Liste := new Zelle'(Inhalt => "Dick und" & (1 .. 24 => ' '), Nachfolger => null);
   Namens_Liste := Temp_Liste;
   Temp_Liste.Nachfolger := new Zelle'(Inhalt => "Doof" & (1 .. 28 => ' '), Nachfolger => null);
   while Namens_Liste /= null loop
      text_io.put_line(Namens_Liste.Inhalt);
      Namens_Liste := Namens_Liste.Nachfolger;
   end loop;
end;


Dieses Beispiel zeigt den grundsätzlichen Aufbau einfach verketteter Datenstrukturen. Die unvollständige Typdeklaration

type Zelle;

ist nötig, da die nachfolgende Zugriffsdeklaration

type Liste is access Zelle;

den Typ Zelle benötigt. Damit kann dann die vollständige Verbunddeklaration für Zelle vereinbart werden.

type Zelle is record
   Info : STRING(1..100);
   L : LISTE;
end record;



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