Fehlerbehandlung

Für Fragen von Einsteigern und Programmieranfängern...
Eclipticon
Beiträge: 292
Registriert: Sa 5. Feb 2011, 20:38
OS, Lazarus, FPC: Windows XP VirtualBox (FPC 2.6.4, Laz 1.2.4)
CPU-Target: 32Bit
Wohnort: Wien

Fehlerbehandlung

Beitrag von Eclipticon »

Guten Morgen!

Gehe ich recht in der Annahme, dass diese zwei Teile Codes prinzipiell das gleiche machen, d.h. ich mir in der zweiten Variante dden finally-Block sparen kann, ASomething aber trotzdem auf jeden Fall freigebe?

Code: Alles auswählen

procedure Test;
var
   ASomething: TSomething;
begin
  try
    try
      ASomething := TSomething.Create;
      ASomething.DoSomething;
      //....
    finally
      ASomething.Free;
    end;
  except
    on E: Exception do ShowMessage(...);
  end;
end;

Code: Alles auswählen

procedure Test;
var
   ASomething: TSomething;
begin
  try
    ASomething := TSomething.Create;
    ASomething.DoSomething;
    //....
  except
    on E: Exception do ShowMessage(...);
  end;
  ASomething.Free;
end;
Danke!

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2846
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von m.fuchs »

Eclipticon hat geschrieben:Gehe ich recht in der Annahme, dass diese zwei Teile Codes prinzipiell das gleiche machen, d.h. ich mir in der zweiten Variante dden finally-Block sparen kann, ASomething aber trotzdem auf jeden Fall freigebe?
Nein, wenn innerhalb deiner except-Behandlung wieder eine Exception geworfen wird, dann wird ASomethingFree; nicht erreicht:

Code: Alles auswählen

procedure Test;
var
   ASomething: TSomething;
begin
  try
    ASomething := TSomething.Create;
    ASomething.DoSomething;
  except
    on E: Exception do raise Exception.Create('maeh');
  end;
  ASomething.Free;
end;
Daher ist die sicherste Variante immer:

Code: Alles auswählen

try
  try
  except
  end;
finally
end;
hth
Michael
0118999881999119725-3

Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Socke »

Eclipticon hat geschrieben:

Code: Alles auswählen

procedure Test;
var
   ASomething: TSomething;
begin
  try
    try
      ASomething := TSomething.Create;
// […]
Du solltest, bevor du dein Objekt erstellst, die Objekt-Variable auf nil setzen. Wird nämlich innerhalb des Konstruktors eine Exception ausgelöst, löst .Free mit Sicherheit noch eine aus…
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Heinrich Wolf
Beiträge: 323
Registriert: Di 12. Apr 2011, 13:21
OS, Lazarus, FPC: WinXP + VMWare Player mit Fedora14, L 1.1, FPC 2.7.1
CPU-Target: 1core 1,8GHz 32Bit
Wohnort: Fürth
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Heinrich Wolf »

Ich dachte, wenn innerhalb des Konstruktors eine Exception ausgelöst wird, dann wird das Result des Konstruktors zurückgegeben. Damit wäre das NIL wieder weg und das Result wäre ein halb initialisiertes Objekt, das mit Free freigegeben werden muss. Dasselbe gilt natürlich für alles, was innerhalb des Konstruktors passiert. Darin kann ja ein weiterer Konstruktor und try stehen. Wenn das nicht so ist, befürchte ich ein Speicherleck, sobald eine Exception im Konstruktor auftritt. Bei news.embarcadero.com lese ich zwar immer folgendes:

Code: Alles auswählen

Object := tObject.Create;
try
  // Arbeite mit Object
finally
  Object.free;
end;
aber ich mache es oft so:

Code: Alles auswählen

Object := Nil;
try
  Object := tObject.Create;
  // Arbeite mit Object
except
  on e : Exception do
    Application.MessageBox(e.Message, Caption, mb_IconHand);
end;
Object.free;
Schade, dass man except nicht mit finally kombinieren kann. Aber Free auf Nil ist ja erlaubt. Ich nehme gern except, weil ich e.Message sehen möchte.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Socke »

Heinrich Wolf hat geschrieben:Ich dachte, wenn innerhalb des Konstruktors eine Exception ausgelöst wird, dann wird das Result des Konstruktors zurückgegeben. Damit wäre das NIL wieder weg und das Result wäre ein halb initialisiertes Objekt, das mit Free freigegeben werden muss.
Wird innerhalb eines Konstruktors eine Exception ausgelöst (gilt auch für dort aufgerufene Funktionen), die nicht gefangen wird, wird direkt der Destruktor aufgerufen. Daher muss dieser auch nicht vollständig erstellte Objekte wieder vollständig freigeben können.
Hinterher hat die Objektvariable den gleichen Wert wie vorher.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Heinrich Wolf
Beiträge: 323
Registriert: Di 12. Apr 2011, 13:21
OS, Lazarus, FPC: WinXP + VMWare Player mit Fedora14, L 1.1, FPC 2.7.1
CPU-Target: 1core 1,8GHz 32Bit
Wohnort: Fürth
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Heinrich Wolf »

Hmm. Wenn der Destruktor automatisch aufgerufen wird, dann führt mein Code zu einer Schutzverletzung, weil das Objekt nicht mehr nil ist, aber schon freigegeben wurde. Wie kommt man da sauber raus? z.B. so?

Wenn man die Exception Message haben will:

Code: Alles auswählen

try
  Object := tObject.Create;
  try
    // Arbeite mit Object
  except
    on e : Exception do
      Application.MessageBox(e.Message, Caption, mb_IconHand);
  end;
  Object.free;  
except
  on e : Exception do
    Application.MessageBox(e.Message, Caption, mb_IconHand);
end;
Wenn man die Exception Message nicht braucht, so wie ich oben news.embarcadero.com zitiert habe.

Heinrich Wolf
Beiträge: 323
Registriert: Di 12. Apr 2011, 13:21
OS, Lazarus, FPC: WinXP + VMWare Player mit Fedora14, L 1.1, FPC 2.7.1
CPU-Target: 1core 1,8GHz 32Bit
Wohnort: Fürth
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Heinrich Wolf »

Heinrich Wolf hat geschrieben:... Bei news.embarcadero.com lese ich zwar immer folgendes:

Code: Alles auswählen

Object := tObject.Create;
try
  // Arbeite mit Object
finally
  Object.free;
end;
...
Dabei wird die Exception, die vom Create kommen kann, nicht abgefangen. Also braucht man auch hier ein zweites try

Code: Alles auswählen

try
  Object := tObject.Create;
  try
    // Arbeite mit Object
  finally
    Object.free;
  end;
except
  on e : Exception do
    Application.MessageBox(e.Message, Caption, mb_IconHand);
end;

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Socke »

Heinrich Wolf hat geschrieben:Hmm. Wenn der Destruktor automatisch aufgerufen wird, dann führt mein Code zu einer Schutzverletzung, weil das Objekt nicht mehr nil ist, aber schon freigegeben wurde. Wie kommt man da sauber raus?
Indem man die Objektvariable vor Aufruf des Konstruktors auf nil setzt.

Die häufigste Ursache für Exceptions innerhalb des Konstruktors ist wohl die unsachgemäße Verwendung einer Klasse, was in einem Produktiv-System nicht auftreten sollte. Wenn kein Speicher mehr reserviert werden kann, tritt die Exception streng genommen schon vor dem Konstruktor auf, aber in dem Fall hat in der Regel dein gesamtes Computer-System Probleme.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Heinrich Wolf
Beiträge: 323
Registriert: Di 12. Apr 2011, 13:21
OS, Lazarus, FPC: WinXP + VMWare Player mit Fedora14, L 1.1, FPC 2.7.1
CPU-Target: 1core 1,8GHz 32Bit
Wohnort: Fürth
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Heinrich Wolf »

Heinrich Wolf hat geschrieben:Hmm. Wenn der Destruktor automatisch aufgerufen wird, dann führt mein Code zu einer Schutzverletzung, weil das Objekt nicht mehr nil ist, aber schon freigegeben wurde.
Socke hat geschrieben:Hinterher hat die Objektvariable den gleichen Wert wie vorher.
Es ist also immer noch nil, weil es durch die Exception nicht zur Zuweisung gekommen ist. Dann gibt es auch keine Schutzverletzung.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Socke »

Heinrich Wolf hat geschrieben:Es ist also immer noch nil, weil es durch die Exception nicht zur Zuweisung gekommen ist. Dann gibt es auch keine Schutzverletzung.
In deinem Code wird die Objektvariable nicht initialisiert und kann daher jeden Wert haben; dass sie ausgerechnet nil ist, hat eine Wahrscheinlichkeit von 1:4294967296.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Heinrich Wolf
Beiträge: 323
Registriert: Di 12. Apr 2011, 13:21
OS, Lazarus, FPC: WinXP + VMWare Player mit Fedora14, L 1.1, FPC 2.7.1
CPU-Target: 1core 1,8GHz 32Bit
Wohnort: Fürth
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Heinrich Wolf »

14:27 Du hast recht!

17:30 ergänzt:
Ich hab meine Code Schnipsel noch mal durchgeschaut. In keinem der Fälle, wo der Konstruktor eine Exception wirft, wird free erreicht. Allerdings ist das Objekt dann nicht notwendiger Weise Nil. Nil wäre natürlich besser für den Code außen herum. Und Object := Nil; nach Object.Free; wäre auch nicht schlecht, auch wenn Rudy Velthuis vom TeamB dagegen wettert.

https://forums.embarcadero.com/thread.j ... adID=57845

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Socke »

Heinrich Wolf hat geschrieben:Nil wäre natürlich besser für den Code außen herum. Und Object := Nil; nach Object.Free; wäre auch nicht schlecht, auch wenn Rudy Velthuis vom TeamB dagegen wettert.
Irgendwann mutiert das zu einer akademischen Frage, deren Antwort für das Software-Design vollkommen irrelevant ist.
Heinrich Wolf hat geschrieben:Ich hab meine Code Schnipsel noch mal durchgeschaut. In keinem der Fälle, wo der Konstruktor eine Exception wirft, wird free erreicht.
Ja, das kommt dann immer auf die konkrete Situation an, wo genau welche Zeile im Code steht.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Eclipticon
Beiträge: 292
Registriert: Sa 5. Feb 2011, 20:38
OS, Lazarus, FPC: Windows XP VirtualBox (FPC 2.6.4, Laz 1.2.4)
CPU-Target: 32Bit
Wohnort: Wien

Re: Fehlerbehandlung

Beitrag von Eclipticon »

Hi,

danke fuer Euer Feedback ... wenn ich nicht gerade Beispielcode schreibe, initialisiere ich meine Objekte auch alle mit nil:

Code: Alles auswählen

var
  ASomething: TSomething = nil;
Hierzu aber noch eine Frage: Objektinstanzen, die Teile eines anderen Objekts sind kann ich nur "zu Fuss" z.B. im constructor mit nil initialisieren, oder?

Danke!

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Fehlerbehandlung

Beitrag von Socke »

Eclipticon hat geschrieben:Hierzu aber noch eine Frage: Objektinstanzen, die Teile eines anderen Objekts sind kann ich nur "zu Fuss" z.B. im constructor mit nil initialisieren, oder?
Der gesamte Speicher eines Objektes wird beim Anlegen der Instanz — also noch vor Aufruf des Konstruktors — mit Nullen überschrieben. Die Felder (Variablen eines Objekts) nochmals mit nil oder Null zu überschreiben hat also wenig Sinn, außer, dass es noch einmal explizit im Benutzer-Code geschieht und man es lesen kann.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
theo
Beiträge: 10999
Registriert: Mo 11. Sep 2006, 19:01

Re: Fehlerbehandlung

Beitrag von theo »

Socke hat geschrieben: Irgendwann mutiert das zu einer akademischen Frage, deren Antwort für das Software-Design vollkommen irrelevant ist.
Schön gesagt.
Ich habe ausserdem oft das Gefühl, dass man das Exception-Handling nicht unbedingt zu Ende denken sollte,
das gibt nur Kopfschmerzen und ist dann meistens in der Praxis umso unbrauchbarer, je mehr man sich überlegt hat. :wink:

Antworten