Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Konstruktor abbrechen


freedy - Mi 30.09.09 14:43
Titel: Konstruktor abbrechen
Hallo Forum,

kennt ihr eine Möglichkeit den Konstruktor zu unterbrechen und eine Instanz nicht anzulegen, wenn beim Anlegen ein Fehler aufgetreten ist?

Ich habe probiert mit Raise eine Exception aufzurufen. Das gibt aber am Programmende nur Probleme mit nicht freigegebenen Speicher.

Ein Beispiel für diesen Bedarf ist z. B. folgendes: der Konstruktor erwartet ein Owner-Objekt. Wenn dieses aber nil ist, soll die Instanz der Klasse nicht angelegt werden. Bisher habe ich mich noch nicht getraut, innerhalb des Konstruktors ein Free aufzurufen, weil ich denke, dass das ein totales Chaos gibt.

Habt ihr einen Rat?

Grüße,
Micha


Tastaro - Mi 30.09.09 14:59


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
type
   EEinFehler = class(exception)
   end;


   TKlasse = class(tobject)
   private
      FIrgendwas: TIrgendwas;
   public
      constructor create(EinObjekt: TEinObjekt);

      destructor Destroy;
                 override;
   end;



implementation


constructor TKlasse.create(EinObjekt: TEinObjekt);
begin
   inherited create;
   FIrgendwas := nil;
   if not assigned(EinObject) then
      raise EEinFehler.create('oO')
   else
      FIrgendwas := TIrgendwas.create;
end;

destructor TKlasse.destroy;
begin
   if assigned(FIrgendwas) then
      FIrgendwas.free;
   inherited destroy;
end;


Beste Grüße


Gausi - Mi 30.09.09 15:07

Ich bin mir da nicht ganz sicher, aber das Free im Constructor scheint erlaubt zu sein.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type TTest = class(TObject)
  constructor Create(f: Boolean);
end;

//...

Constructor tTest.Create(f: Boolean);
begin
  if not f then free;
end;

// ...

TTest.Create(false); // erzeugt weder Fehler zur Laufzeit noch Memoryleaks am Ende
TTest.Create(True); // erzeugt Memoryleak


Tastaro - Mi 30.09.09 15:23

Wenn man den Konstruktor mit einer Exception abbricht gibt es meines Wissens und meiner Erfahrung nach kein Memoryleak.

Beste Grüße


freedy - Mi 30.09.09 15:37

user profile iconTastaro hat folgendes geschrieben Zum zitierten Posting springen:
Wenn man den Konstruktor mit einer Exception abbricht gibt es meines Wissens und meiner Erfahrung nach kein Memoryleak


Richtig. Habe ich nochmal mit einer Testklasse probiert. Dann muss mein Fehler irgendwo anders liegen. Jedenfalls bekomme ich bei Abbruch zwei Memoryleaks (1x String, 1x Exception).

Vielen Dank erstmal.


dummzeuch - Mi 30.09.09 21:25

user profile iconfreedy hat folgendes geschrieben Zum zitierten Posting springen:

kennt ihr eine Möglichkeit den Konstruktor zu unterbrechen und eine Instanz nicht anzulegen, wenn beim Anlegen ein Fehler aufgetreten ist?

Ich habe probiert mit Raise eine Exception aufzurufen. Das gibt aber am Programmende nur Probleme mit nicht freigegebenen Speicher.


Wenn man im Konstruktor eine Exception ausloest, so wird automatisch der zugehoerige Destruktor aufgerufen. Dh. der Destruktor muss damit klarkommen, dass evtl. das Objekt nur teilweise erzeugt ist. Das ist in der Regel kein Problem, weil alle Felder mit 0 / NIL / Leerstring initialisiert werden, also folgendes einfach nur funktioniert:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
constructor TMyObject.Create;
begin
  inherited Create;
  raise Exception.Create('Test');
  FMyList := TList.Create;
end;

destructor TMyObject.Destroy;
begin
  FMyList.Free;
  inherited;
end;


Da FMyList mit NIL initialisiert wird, kann man im Destruktor einfach FMyList.Free (oder alternativ FreeAndNil(FMyList)) aufrufen, da die Methode Free ueberprueft, ob SELF <> NIL ist.

Der langen Rede kurzer Sinn: Exceptions sind durchaus dazu geeignet, die Erzeugung eines Objekts im Konstruktor zu verhindern. Bei korrekten Destruktoren sollte es dabei auch keine Speicherlecks geben.

twm


BenBE - Do 01.10.09 09:41

Innerhalb des Konstruktors ist der Aufruf der Funktion Fail erlaubt. Dabei wird eine Exception ausgelöst, die bereits angelegte Instanz wieder freigegeben und die Kontrolle an den nächst-höheren Exception-Handler gegeben.