Lebenszeit und Sichtbarkeit der Variablen-Klassen

Aus Wiki des Deutschsprachige Xbaseentwickler e. V.
Zur Navigation springen Zur Suche springen

Übersicht der Speicherklassen

Speicherklasse erzeugt durch zerstört durch
PUBLIC PUBLIC RELEASE
PRIVATE PRIVATE / Zuweisung RELEASE
STATIC STATIC nicht möglich
LOCAL LOCAL / () nicht möglich
FIELD FIELD nicht möglich


Lebensdauer

dynamische Variablen

PUBLIC

Eine PUBLIC Variable existert ab dem Moment, in dem sie mittels PUBLIC deklariert wird.

Die Lebenszeit endet in dem Moment, in dem das Programm endet, oder die Variable explizit durch eine RELEASE Anweisung aus dem Speicher entfernt wird.


PRIVATE

Eine PRIVATE Variable existiert ab dem Moment, in dem sie mittels PRIVATE deklariert wird, oder indem einer nicht existierenden Variablen ein Wert zugewiesen wird.

Die Lebenszeit einer PRIVATE Variablen endet in dem Moment, indem die Callstack-Ebene verlassen wird, in der die Variable erzeugt wurde:

Main()
+-> Func1()
    +-> Func2()
          PRIVATE cHugo

In dem Moment, indem Func2() endet, wird cHugo aus dem Speicher entfernt.


lexikalische Variablen

STATIC

Eine STATIC Variable existiert ab Programmstart, sie ist jedoch nur in ihrem jeweiligen Kontext sichtbar.


LOCAL

Eine LOCAL Variable existiert ab Programmstart, sie ist jedoch nur in ihrem jeweiligen Kontext sichtbar.

FIELD

Eine FIELD Variable existiert ab Programmstart, sie ist jedoch nur in ihrem jeweiligen Kontext sichtbar.

Sichtbarkeit

dynamische Variablen

PUBLIC

Ab ihrer Erzeugung ist eine PUBLIC Variable im ganzen Programm sichtbar.


PRIVATE

Ab ihrer Erzeugung ist eine PRIVATE Variable auf ihrer Callstack-Ebene sichtbar, sowie in allen Funktionen, die nach ihrer Erzeugung und vor dem Ende der Funktion, in der sie erzeugt wurde, aufgerufen werden:

Main()
+-> Func1()
    +-> Func2()
          PRIVATE cHugo
        +-> Func3()
        +-> Func4()

Die Variable cHugo ist in Func2(), Func3() und Func4() sichtbar.


lexikalische Variablen

Lexikalische Variablen werden durch den Compiler angelegt und sind damit ab Programmstart verfügbar, wenn sich das Programm im entsprechenden Kontext befindet.


STATIC (definiert vor der ersten Funktion, Prozedur, Klasse)

STATIC, die in einer Programmdatei ganz zu Anfang definiert werden, sind in dieser Programmdatei sichtbar.


STATIC (definiert innerhalb einer Funktion, Prozedur, Klasse)

STATIC, die innerhalb einer Funktion etc. definiert werden, sind auch nur in dieser Funktion sichtbar. Im Gegensatz zu LOCAL Variablen behalten sie ihren Wert aber bei, wenn die Funktion verlassen wird.


LOCAL

LOCAL Variablen sind nur in der Funktion sichtbar, in der sie definiert sind. Sie sind nicht in aufgerufenen Funktionen sichtbar (man kann sie zwar als Parameter übergeben, aber das hat nichts mit Sichtbarkeit der ursprünglichen Variablen zu tun), und mit dem Verlassen der Funktion, in der sie definiert wurden, verlieren sie den ihnen zugewiesenen Wert.

Überlagerung

Was passiert, wenn dynamische und lexikalische Variablen mit gleichem Namen aufeinander treffen wie in diesem Beispiel:

FUNCTION Main()
  cHugo := "Hugo"
  Func1()
  ? cHugo
RETURN (.T.)
FUNCTION Func1()
  Local cHugo
  cHugo := 88
  ? cHugo
RETURN (.T.)

Was wird in der letzten Zeile von Func1() ausgegeben?

88

Um diese Antwort zu verstehen, muss man sich überlegen, was der Compiler tut. In der Funktion Func1() wird eine lexikalische Variable cHugo definiert. Der Compiler nimmt nun alle Bezüge auf diese Variable und mappt sie auf den gleichen Speicherbereich. Danach verbleibt in Func1() kein Verweis mehr auf cHugo.

Zur Laufzeit ist cHugo in Func1() nicht sichtbar.

Jetzt ändern wir das Beispiel ein wenig ab:

FUNCTION Main()
  cHugo := "Hugo"
  Func1()
  ? cHugo
RETURN (.T.)
FUNCTION Func1()
  Local cHugo, bBlock
  bBlock := "{|| cHugo}"
  bBlock := &bBlock
  cHugo := 88
  ? Eval(bBlock)
RETURN (.T.)

Diesmal lautet das Ergebnis

Hugo

Warum? Der Codeblock wird zur Laufzeit durch die Laufzeitumgebung compiliert: zuerst erzeugen wir ein Literal, das danach compiliert wird. Zur Laufzeit gibt es keinen Bezug zu der LOCAL cHugo mehr, so dass beim Eval(bBlock) im dynamischen Variablenverzeichnis nach cHugo gesucht wird, und auch gefunden wird. Und damit wird dann der Wert der PRIVATE Variablen cHugo ausgegeben.

FUNCTION Main()
  cHugo := "Hugo"
  Func1()
  ? cHugo
RETURN (.T.)
FUNCTION Func1()
  Local cHugo, bBlock
  bBlock := {|| cHugo}
  cHugo := 88
  ? Eval(bBlock)
RETURN (.T.)

Und jetzt?

88

Da der Codeblock zur Compile-Zeit definiert ist, kann ihn der Compiler übersetzen und tut dies auch, indem die Referenz auf cHugo lexikalisch interpretiert wird.

Das Verhalten gilt unabhängig davon, ob die dynamische Variable als PRIVATE oder PUBLIC definiert ist, und es ist auch egal, ob die lexikalische Variable LOCAL oder STATIC ist.

Viele Probleme und Missverständnisse beim Einsatz von dynamischen und lexikalischen Variablen haben an dieser Stelle ihre Ursache.

der M-> (bzw. MEMVAR->) Operator

Wenn explizit auf eine dynamische Variable zugegriffen werden soll, kann der M-> bzw. MEMVAR-> Operator verwendet werden. Dieser weist den Compiler an, für diesen Zugriff explizit eine dynamische Variable zu verwenden und nicht eine eventuell existierende lexikalische Variable:

FUNCTION MAIN
   PRIVATE cHugo
   TueIrgendEtwas()
RETURN(.T.)
FUNCTION TueIrgendEtwas()
   LOCAL cHugo
   cHugo := 123
   M->cHugo := "Heute"
   ? cHugo
   ? M->cHugo
   ? MEMVAR->cHugo
   WAIT
RETURN(.T.)

Dieses kleine Programm liefert diesen Output:

     123
Heute
Heute
Press any key to continue ...