Einzelnen Beitrag anzeigen

Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#7

Re: boolsche Variable abfragen, aber wie genau(Anfängerfrage

  Alt 29. Aug 2006, 09:33
Zitat von juergen:
Muss eine boolsche Variable überhaupt initialisiert werden?
Hi,
ich denke hier sollte nocheinmal klar gesagt werden, dass du boolsche Variablen immer initialisieren solltest! Hier wurde zwar schon gesagt, dass die sonst undefiniert sind, aber die Konsequenzen sollten einem auch klar sein.
Instanzvariablen (also Variablen die zu einer Klassen-Instanz gehören) werden immer initialisiert. Hast du hier einen Boolschen Wert ist der auch initialisiert, die Frage ist also nur True oder False?
Da man mit boolschen Werten i.d.R. einen Zustand anzeigt und man diesen wiederum für den Programmstart leicht festlegen kann, sollte man hier die Variable auch im Konstruktor initialisieren.
Verzichtet man darauf, schafft man sich einen Fehler der nicht immer leicht zu finden ist.

Das Problem lässt sich sogar verallgemeinern! Du solltest jede Variable initialisieren! Das der Wert einer Variablen nicht definiert ist störrt dein Programm sehr wenig. Legst du eine lokale Variable an, so wird einfach ein Speicherbereich genommen, der gerade frei ist, in den diese Variable reinpasst. Natürlich wird der gleiche Speicher immer wieder mal von irgendeinem Programm benutzt (darum kümmert sich Windows). Wird ein Programm / eine Prozedur beendet, werden die verwendeten Speicherbereiche nur als frei markiert. Das heißt die alten Werte stehen hier immer noch.
Ja, dein Programm macht auch das, was es immer tut: Es nimmt den Speicher und interpretiert den vorgefundenen Wert. Mit etwas Glück startest du das Programm und es kracht. Mit etwas Pech passiert nichts und alles läuft wie erwartet.
Gut, letzteres klingt erstmal gar nicht so schlimm. Das Problem ist nur, du hast ein Programm das nicht mehr terminiert. Irgendwann (gerne beim Kunden) ist der Speicher mal mit einem falschen Wert initialisiert, was dann? Hier kann es zu einer Exception kommen, aber auch zu einer Schleife die einfach mal 2.000.000.000 Durchläufe mitmacht oder zu allem anderen.
Hier den Fehler zu finden ist auch um einiges schwieriger, da es ein semantischer Fehler ist, der sich nicht mal ohne weiteres reproduzieren lässt. Je nachdem welcher Wert gerade im entsprechenden Speicher steht geht alles gut oder es treten ganz verschiedene Fehler auf.

Delphi-Quellcode:
procedure doFoo;
var i : Integer;
begin
  // ganz schlecht
  // i wird nicht initialisiert, könnte auch -2^{31} sein
  // das wären eine ganze Menge durchläufe!
  while (i < 10) do
  begin
  end;
  
  // besser:
  i := 0;
  while (i < 10) do
  begin
  end;

  // oder
  for i := 0 to 10 do
  begin
  end;
end;

procedure doMoreFoo;
var bitmap : TBitmap;
begin
  if assigned(Bitmap) then
  begin
    // tja, sehr hohe Chance hier zu landen
    // natürlich wurde die Bitmap nicht angelegt!
    // aber assigned prüft ob die Referenz <> nil ist
    // nil ist dabei die Adresse 0x00000000
    // Das heißt es gibt 2^{32} - 1 Möglichkeiten die ungleich 0 sind.
    // da auf die 0 zu hoffen ist schlecht!
  end;
end;
Deshalb ist die goldene Regel alles zu initialisieren, was man verwendet! Das gilt insbesondere dann auch wieder, wenn du eine Variable freigibst, die noch gelesen werden könnte (und damit implizit für alle Instanzvariablen, die eine Klasse als Datentyp haben). Wenn du diese mittels Free freigibst, dann wird auch schön der Speicher als frei markiert, in dem die Variable lag. Die Referenz die du als Variable speicherst, die wird nicht verändert. Für eine solche Variable ist es nicht möglich zu sagen, ob sie gültig ist oder nicht. Genaugenommen musst du denken die ist gültig. Die zeigt dann auf einen freien (oder anders verwendeten) Speicher und das geht auch nicht gut! Deshalb beim freigeben solcher Variablen FreeAndNil verwenden.

Delphi-Quellcode:
type
  TMyClass = class(TObject)
    private
      FBitmap : TBitmap;
    protected
      procedure createBitmap;
      procedure doFoo;
      procedure FreeBitmap;
  end;

...

procedure TMyClass.createBitmap;
begin
  // Fehler1 : nicht freigeben der alten Bitmap!
  self.FBitmap := TBitmap.Create;
  self.FBitmap.Width := 512;
  self.FBitmap.Height : 512;
  self.FBitmap.PixelFormat := pf32Bit;
  
  // so, würde zweimal hintereinander createBitmap aufgerufen werden,
  // würde die erste Instanz bis zum Programmende im Speicher liegen
  // das heißt man verschwendet hier 4 Byte * 512 * 512, also ein kleines MByte
  // wenn ich diese Operation jetzt mehrfach aufrufe....


  // besser:
  // erst aufräumen
  FreeAndNil(self.FBitmap);

  // dann neu anlegen
  self.FBitmap := TBitmap.Create;
  self.FBitmap.Width := 512;
  self.FBitmap.Height : 512;
  self.FBitmap.PixelFormat := pf32Bit;
end;

procedure TMyClass.FreeBitmap;
begin
  // 1:
  self.FBitmap.Free;

  // 2:
  FreeAndNil(self.FBitmap);
end;

procedure TMyClass.doFoo;
begin
  // mögliche Probleme nach Aufruf von
  // FreeBitmap
  if assigned(self.FBitmap) then
  begin
    // Probleme variieren zwischen 1 und 2
    // 2 funktioniert immer wie gewünscht!

    // bei 1 wird nur der Speicher auf den self.FBitmap zeigt freigegeben
    // die Adresse, die implizit in self.FBitmap steht bleibt erhalten
    // wurde hier also mittel createBitmap einmal eine gültige Adresse zugewiesen,
    // so würde diese Bedingung wahr sein

    with self.FBitmap do
    begin
      ...
      // dies würde jetzt für 1 zu einem Problem werden!
      // es gibt schließlich keine Bitmap mehr!
    end;
  end;
end;
  Mit Zitat antworten Zitat