Variablen-Klassen
Allgemeine Definition
Wir sprechen hier von den Speicherklassen der Variablen. Dabei gibt es zwei verschiedene Formen zu unterscheiden:
- dynamische Variablen ( PUBLIC,PRIVATE), auch Memory-Variablen genannt
- lexikalische Variablen (LOCAL, STATIC, FIELD, MEMVAR)
Dynamische Variablen
Dynamische Variablen werden - wie der Name besagt - dynamisch zur Laufzeit angelegt. Gerade die Tatsache, dass zumindest die PRIVATE Variablen durch eine einfache Zuweisung erzeugt werden, lassen sie augenscheinlich "ideal" erscheinen, um ad-hoc eine Variable zu definieren, die man gerade mal braucht.
Ihre "Sichtbarkeit" führt aber dazu, dass sie oft zu ungewollten Nebeneffekten führen können:
FUNCTION Main() FOR nI := 1 TO 10 ? ProcName() + " " + Str(nI, 5) TueIrgendWas() NEXT RETURN(.T.) FUNCTION TueIrgendWas() FOR nI := 1 TO 20 ? ProcName() + " " + Str(nI, 5) NEXT RETURN(.T.)
Da nichts anderes angegeben ist, wird in Main() durch die FOR Anweisung die PRIVATE Variable nI erzeugt. In der Funktion TueIrgendWas() würde theoretisch (!) auch eine PRIVATE Variable nI erzeugt, aber: das Laufzeitsystem prüft erst, ob es bereits eine solche Variable gibt (in diesem Fall entweder PUBLIC oder PRIVATE), und da es eine solche Variable bereits gibt, wird die existierende Variable verwendet. Nach der FOR NEXT Schleife hat nI den Wert 21, der auch nicht mehr geändert wird. Wenn die Steuerung nach Main() zurückkehrt, wird die FOR NEXT Schleife direkt verlassen, da der Wert der Variablen nI den Wert 21 hat.
Dies lässt sich theoretisch vermeiden, indem in TueIrgendWas() nI explizit über die PRIVATE Anweisung erzeugt wird. Dann gibt es zwei Variablen dieses Namens, von denen die in Main() erzeugte durch die in TueIrgendEtwas() definierte "überlagert" wird.
Im Gegensatz dazu ist ein PUBLIC Variable sichtbar, sobald sie durch eine PUBLIC Anweisung erzeugt wird. Ähnlich wie in obigem Beispiel kann sie durch eine explizit erzeugte PRIVATE Variable "überlagert" werden.
In Xbase++ sind die Anweisungen zur Erzeugung von dynamischen Variablen (PUBLIC bzw. PRIVATE) ausführbare Anweisungen.
Lexikalische Variablen
Diese Variablen-Klasse bezeichnet Variablen, die zur Compile-Zeit definiert und verwendet werden. Zu einer späteren Zeit ist ein Zugriff z.B. über ad-hoc Codeblöcke oder Makros (im Gegensatz zu dynamischen Variablen) nicht möglich.
Eine LOCAL Variable ist immer nur in der Function/Procedur/Method sichtbar, in der sie deklariert wird. (Eine LOCAL Variable kann als Parameter an weitere Funktionen übergeben werden, aber das hat nichts mit der Frage der Sichtbarkeit der ursprünglichen Variablen zu tun.)
Analog dazu ist eine STATIC Variable immer nur in der Programm-Quelle sichtbar, in der sie deklariert wird. Hier sind zwei Arten zu unterscheiden:
programmweit sichtbare STATIC
Eine solche STATIC wird zu Beginn der Programm-Quelle, vor jeder ausführbaren Anweisung, definiert und ist dadurch in dieser Programm-Quelle sichtbar:
STATIC lSchliessen := .F. FUNCTION Main() Local nEvent, mp1, mp2, oXbp WHILE nEvent <> xbeP_Close .and. !lSchliessen nEvent := AppEvent(@mp1, @mp2, @oXbp) oXbp:handleEvent(nEvent, mp1, mp2) END RETURN(.T.)
auf eine Funktion beschränkte STATIC
FUNCTION TueIrgendEtwas() LOCAL nLen, nI STATIC lSchliessen IF lSchliessen <> NIL .AND. lSchliessen RETU(.T.) ENDIF lSchliessen := .F. ... IF <Bedingung> lSchliessen := .T. ENDIF RETURN(.T.)
Diese Art von STATIC wird meist verwendet, um einen bestimmten Status innerhalb einer Funktion auch dann festzuhalten, wenn die Funktion beendet ist und per Definition alle darin definierten Variablen dem Garbage Collector zum Opfer gefallen sind.
Während LOCAL Variablen mit dem Aufruf einer Funktion neu initialisiert werden (entweder NIL oder ein in der LOCAL Anweisung explizit vorgegebene Wertzuweisung), geschieht dies mit STATIC Variablen nicht, da sie ja ihren Wert behalten sollen.
Was soll man verwenden?
Das ist schon fast eine Glaubensfrage.
Lexikalische Variablen haben den Vorteil, dass sie deutlicher weniger Nebenwirkungen haben, wenn man das Beispiel der PRIVATE Variablen oben betrachtet.
Dazu kommt, dass der Compiler LOCAL und STATIC zu Speicher-Adressen auflöst und während der Laufzeit nur mit den Speicher-Adressen arbeitet und nicht mit Variablen-Namen.
Im Vergleich dazu müssen PRIVATE und PUBLIC Variable in einem dynamischen Wörterbuch nachgeschlagen werden:
- gibt es diese Variable?
- wenn ja, welchen Wert hat sie?
Diese Aktionen kosten bei der Laufzeit zusätzlich Zeit.
Die Entscheidung liegt aber - wie so oft - beim Programmierer.