Verehrtester Freund,

ich bin untröstlich über deine Verzweiflung. Allerdings verstehe ich deine Verwirrung nicht ganz - hast du mein Grundprinzip vergessen, nur das zu vererben, was auch sichtbar ist? Um dir auf die Sprünge zu helfen, wollen wir mein und dein Programm Schritt für Schritt gemeinsam durchgehen. Du schreibst, du habest dasselbe gemacht wie ich. Am Ende wird dir hoffentlich klar sein, dass, was du gemacht hast, nur vermeintlich dasselbe ist.

Der Typ Wurzel ist nicht sichtbar von Ada.Finalization.Controlled abgeleitet, daher sind die im privaten Teil von Geheimniskrämer definierten Operationen nur dort vererbbar, wo sie auch sichtbar sind, das heißt nur auf Kinder.

Nun ist Abgeleitet ein Kind von Geheimniskrämer, und somit erbt Etwas und ersetzt die primitiven Operationen Initialize, Adjust, Finalize.

Im Programm Ausprobieren geschieht darum alles so, wie von mir beschrieben. Dass die ererbten Operationen im privaten Teil (und somit unsichtbar) überschrieben worden sind, tut nichts zur Sache.

Nun wollen wir zu deinem Programm kommen. Dein Paket Neuling hat die identische Spezifikation wie mein Paket Geheimniskrämer.Abgeleitet, daher deine Behauptung, es sei dasselbe, nur - es ist kein Kind von Geheimniskrämer, wie sein Name auch eindeutig erklärt. Somit erbt Neuling.Etwas (im Gegensatz zu Geheimniskrämer.Abgeleitet.Etwas) nichts und kann auch die primitiven Operationen Initialize, Adjust, Finalize nicht überschreiben, siehe RM_95 7.3.1(6). Diese Deklarationen ersetzen folglich nichts, sie führen lediglich neue Prozeduren ein, die rein zufällig die Namen Initialize, Adjust, Finalize tragen.

Bei der Elaborierung deines Programms Überraschung wird Neuling.Nichts erzeugt. Da das Erweiterungsaggregat von Nichts einen Wurzel-Teil enthält, wird dessen Initialize-Operation gerufen. Das RM erlaubt und AI95_00083 erzwingt die Erzeugung des Initialwertes "an Ort und Stelle", so dass keine Notwendigkeit besteht, das Aggregat zu kopieren und auszurichten. Daher ist alles, was geschehen sollte, ein einziger Aufruf von Initialize mit der Ansichtsumwandlung Wurzel(Nichts) als aktuellem Parameter.

Bei der Elaboration von Überraschung.Ding wird wieder Initialize für den Wurzel-Teil gerufen.

Bei der Zuweisung Ding:=Nichts; ist der Wurzel-Teil von Ding kontrolliert, der daher finalisiert werden muss; dann wird Nichts auf Ding kopiert, und abschließend muss wieder der Wurzel-Teil von Ding ausgerichtet werden.

Beim Verlassen des Gültigkeitsbereichs muss erneut der Wurzel-Teil von Ding finalisiert werden. Bei Programmende schließlich muss der Wurzel-Teil von Nichts finalisiert werden.

Du siehst nun hoffentlich, lieber Freund, wo die Unterschiede unserer scheinbar gleichen Programme verborgen liegen. Sie wären tatsächlich gleich, wenn ich nur die Deklaration von Wurzel sichtbar von Ada.Finalization.Controlled abgeleitet hätte. Dass die Ersetzung der primitiven Operationen Initialize etc. im privaten Teil und somit unsichtbar geschieht, spielt dabei bemerkenswerterweise überhaupt keine Rolle. Um das zu verstehen, musst du dir nur im Sprachmanual im Kapitel 7.6 die Spezifikation von Ada.Finalization anschauen - die primitiven Operationen RM7.6(6) Initialize, Adjust, Finalize sind sichtbar und bleiben es auch für alle Zeit, da erneutes Verbergen sichtbar ererbter Operationen unmöglich ist.

Vielleicht wirst du dich jetzt fragen, ob es überhaupt einen Unterschied macht, ererbte Operationen sichtbar oder unsichtbar zu überschreiben (merke wohl: Ich rede nicht vom Hinzufügen).

Die überraschende Antwort ist: Sehr wenig.

In der Tat fällt mir nur ein etwas sonderbarer Effekt ein, der die Umbenennung von Parameternamen betrifft. Um bei unserem Beispiel zu bleiben: Der ursprüngliche Name in RM7.6(6) lautet Object, ich habe ihn nach außen unsichtbar in Objekt umbenannt. Bei namentlicher Parameterassoziation muss nun Initialize(Objekt=>X) beziehungsweise Initialize(Object=>X) geschrieben werden, je nach der lokalen Sichtbarkeit der Deklaration. [Ich befürchte, einige Compiler werden da ihre Schwierigkeiten haben.]

Ich hoffe, mit meinen Ausführungen nicht weiterer Verwirrung Vorschub geleistet zu haben und verbleibe mit besten Wünschen für das neue Jahrtausend (auch hier herrscht weitverbreitete Verwirrung - eigentlich ist es noch gar nicht so weit)

Deine Lady Ada


Ada 2005: [not] overriding

Ada 2005 bietet eine Lösung zum Erkennen solcher unabsichtlicher Fehler, dass neu definierte Operationen ererbte ungewollt (nicht) überschreiben: das neue Schlüsselwort overriding (RM 8.3.1). Du, verzweifelter Neuling, solltest deine (primitiven) Unterprogramm-Deklarationen bei Vererbung in Zukunft stets hiermit verzieren. Hättest du nämlich

  overriding procedure Initialize (Objekt: in out Etwas);

geschrieben und entsprechend für die anderen Operationen, so hätte der Compiler das Programm zurückgewiesen und dir somit klar gemacht, dass deine Erwartung nicht stimmt. Umgekehrt bedeutet not overriding, dass dir der Compiler auf die Finger klopft, wenn die Operation dennoch eine bestehende überschreibt. In deinem Fall muss die Deklaration also wie folgt lauten:

  not overriding procedure Initialize (Objekt: in out Etwas);

Auch deine Lady Ada verspricht, in Zukunft alle solche Deklarationen (wie hier in Geheimniskrämer) entsprechend zu verzieren.


Ada Magica
Inhaltsverzeichnis
© Copyright 2000, 2009 C.K.W. Grein