Wie kann ich einen Destruktor / Konstruktor abbrechen?
Als Beispiel für einen Destruktorabbruch:
Wenn der Benutzer das Programm beendet, dann soll er gefragt werden ob er die Änderungen vorher noch speichern will, dies ist kein Problem.
ABER er soll auch die Möglichkeit haben, den Vorgang abzubrechen: wie kann ich also dafür sorgen, das die Beendung des Programmes zurückgenommen wird?
EXIT funktioniert NICHT!!!
Als Beispiel für den Konstruktorabbruch:
Bei der Erstellung einer Textur wird im Konstruktor das Format überprüft, ob es unterstützt wird bzw. ob ich eine Konvertierung für OpenGL eingebaut habe.
Wie kann ich da den Konstruktor abbrechen?
EXIT funktioniert hier ebenfals nicht und "Destroy" löst eine Zugriffsverletzung aus. Als Idee hätte ich diese Textur irgendwie erst nach Beendigung des Konstruktors zu zerstören, aber mir fällt keine passende Methode dazu ein.
Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
das mit destroy ließe sich lösen indem du Destroy bei allen betreffenden Klassen mit einer anderen Methode aufrufst
Code:
TMyClass =class
BeforeDestroy :procedure; bzw procedureofObject; musst du gucken was besser in deinen Code passt
End;
procedure SaveWaring;
Begin
// Hier würde dann deine Speichern-Abfrage hinkommen
End;
procedure TMyClass.Bla;
Begin
ifAssigned(BeforeDesroy)then BeforeDestroy;
Destroy;
// an dieser Stelle empfielt es sich eigenlich Free zu nehmen aber
//da du von Destroy gesprochen hast...
End;
var MyClass : TMyClass;
Begin
MyClass := TMyClass.Create;
MyClass.BeforeDestroy:= SaveWarning;
MyClass.Bla;// hier wird die klasse wieder freigegeben
End;
Wenn du die VLC Application Klasse benutzt brauchst du daran nichts mehr zu ändern diese sollte das eigentlich schon von haus aus können(bin mir aber net sicher könnte auch das VLC Fenster gewesen sein)
zu dem Create mir fällt nichts anderes ein als
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Uhm. Wenn du mit der VLC arbeitest und es sich hierbei um den Klick auf das X einer Form handelt, dürfte das CloseQuery-Event (oder heißt es OnClose?) genau das sein, was du suchst.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
raise EAbstractError.Create('Please use the other constructor');
end;
classfunction TTest.Create(doit:Boolean): TTest;
begin
if doit then
Result:=inherited Create
else
Result :=nil;
end;
Da ist zwar trotzdem noch eine Exception drin, allerdings nur um zu ermahnen das "richtige" Create zu verwenden, weil man das ursprüngliche Create nicht einfach löschen kann.
Ansonsten scheint es zu funktionieren.
Das "inherited Create" kann man denke ich dann auch gegen einen eigenen Constructor z.B. im "private"-Teil ersetzen.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Es hindert einen auch nichts daran, einen zweiten Konstruktor zu deklarieren.
Soweit ich weiss wird der Speicher des Objektes aber auch wieder freigegeben, wenn in Create ne Exception geworfen wurde. Wenn ich mich irre... Könnte man das ganze über einen einigermaßen guten Stil in Destruktor und Konstruktor ausgleichen. D.h. bevor eine Exception auftreten kann/soll alle Felder des Objektes, die Pointer o.ä. beeinhalten, auf nil setzen. Und im Destruktor diese dann nur, wenn sie gleich nil sind freigeben, bzw. bei TObject-Nachfahren (also generell bei allen Objekten) die Free-Methode nehmen. Die prüft das automatisch (sollte man sowieso immer verwenden anstatt direkten aufruf von Destroy).
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Das Create wird erst dann aufgerufen, wenn deine Instanz bereits erstellt wurde. Der Konstruktor dient nur dazu de Member deiner Klasse zu initialisieren. Eine Exception innerhalb des Konstructors sorgt dafür, dass der Destruktor aufgerufen wird und deine Instanz aus dem Speicher entfernt wird. Also falls du dort andere Dinge wieder löscht, dann pass auf dass da kein Scheiß passiert. Wie Lord schon sagte. Alles was problematisch ist im Konstrukter auf nil setzen. Wobei Member von Klassen eigentlich immer nil sind. Allerdings Exceptions solltest du nur dann werfen, wenn es sich wirklich um eine Ausnahme handelt. Also in der Regel braucht man Exceptions im Konstruktor nur dann wenn sowieso irgendwie schon alles im Eimer ist. Da solltest du meiner Meinung nach immer einen anderer Weg bevorzugen.
OnCloseQuery und OnClose: Das gibts Beides. OnCloseQuery wird vor OnClose aufgerufen. Wenn OnCloseQuery bereits false ergibt wird das beenden abgebrochen.
[edit] Du kannst natürlich auch mehrere Konstruktoren in einer Klasse erstellen. Also das was du da hast sieht für mich eher nach so etwas aus. Du kannst auch Methoden vitual (abstract) machen. Dann kannst du die Methoden in späteren Klassen implementieren.
Um auch mal meinen Senf dazu zu geben:
Im Destruktor zu prüfen, ob die Änderungen gespeichert werden sollen bzw. ob das Beenden abgebrochen werden soll, halte ich für den falschen Weg. Das sollte schon vorher passieren. Also bevor .Destroy überhaupt aufgerufen wird.
Beim Destruktor muss man sich darauf verlassen können, dass er ordnungsgemäß durchläuft und keine Geschäftslogik enthält.
Wie wär's denn alternativ hiermit?:
Code:
unit Unit1;
interface
uses
Classes, SysUtils;
type
{* Classes implementing this interface are able to report whether they have
unsaved changes and allow for a more specific handling of their
destruction. }
ISaveable =interface
{* Saves the state of the implementing class }
procedure Save;
{* Returns true if the component has unsaved changes. }
function hasUnsavedChanges:boolean;
{* Handles the shutdown of this class. If it has unsaved changes, it will
allow the user to choose between various closing strategies such as
[Save And Close], [Dismiss And Close], [Cancel]. }
procedure handleClose;
end;
{ TFancyGUIComponent }
TFancyGUIComponent =class(ISaveable)
private
procedure CloseAndSave;
public
// ISaveable
procedure Save;
function hasUnsavedChanges:boolean;
procedure handleClose;
end;
Denke, das gibt schon ne Idee, wie man's machen könnte. Kerngedanke ist schlicht, dass, wenn man .Free bzw. .Destroy aufruft, es schon feststeht, dass die Klasse nun auch wirklich freigegeben wird.
_________________ "Für kein Tier wird so viel gearbeitet wie für die Katz'."
Also erstmal danke für die Zahlenmässig großen Hinweise.
Also ich hatte beim Formular OnClose verwendet, aber OnCloseQuery ist dafür wohl besser geeignet (das bestätigt letztlich auch die eingebaute Hilfe).
Damit wäre das Problem mit dem Destruktor gelöst.
Zu dem Konstruktor: ich soll also einfache eine "künstliche" Exception auslösen um den Konstruktor zu stoppen? Sieht doch blöd aus... was ist wenn ich die Exception mit Try-Except abfange, damit der Nutzer davon nichts merkt? Wird dann immernoch das Objekt erstellt?
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Sobald du eine Exception im Konstruktor hast ist das Objekt weg, da dann der Destruktor aufgerufen wird. Mit try except kannst du dann verhindern, dass die Exception angezeigt wird. Aber das halt ich für den falschen Weg. Exceptions sind Ausnahmen. Die sollten nur ausgelöst werden, wenn wirklich was im Argen ist. Wenn es nur eine Statusinformation ist, dann ist das nicht die feine Englische.
Alternativ zum Abschießen deine Konstruktors kannst du a) vorher überprüfen ob deine Klasse erstellt werden soll oder b) du erstellst die Klasse rufst eine Methode auf mit der du erfragen kannst, ob das unterstützt wird was du haben willst. Und wenn nicht, dann löscht du die Instanz wieder.
Je nachdem was du für dieses erfragen brauchst kannst du damit auch Klassenfunktionen nehmen. Die benötigen keine erstellte Instanz. Dürfen aber auch nicht auf Member zugreifen.
PS: Exceptions im Destruktor ändern glaube ich auch nicht daran, dass das Objekt gelöscht wird. Das wird es meiner Meinung nach in jedem Fall sobald Destroy/Free aufgerufen wird.
Mitglieder in diesem Forum: 0 Mitglieder und 4 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.