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;