Ich habe gerade einen sehr kleinen Fehler in unserem Code behoben, der durch das Aufschneiden einer Ausnahme verursacht wurde, und ich möchte jetzt sicherstellen, dass ich genau verstehe, was passiert ist.
Hier ist unsere Basisausnahmeklasse, eine abgeleitete Klasse und relevante Funktionen:
%Vor%Der Fehler war natürlich, dass outcallcall eine Exception anstatt einer Derived ausgibt. Mein Fehler resultierte aus höher im Call-Stack Versuche, den Abgeleiteten Fehler zu fangen.
Nun möchte ich nur sicherstellen, dass ich verstehe - ich glaube, dass in der Zeile "throw e" ein neues Exception-Objekt erstellt wird, das einen Standard-Copy-Konstruktor verwendet. Geht das wirklich so?
Wenn ja, darf ich Kopierkonstruktoren für Objekte ausschließen, die geworfen werden? Ich würde wirklich bevorzugen, dass dies nicht wieder passiert, und unser Code hat keinen Grund, Exception-Objekte (die ich kenne) zu kopieren.
Bitte, keine Kommentare zu der Tatsache, dass wir unsere eigene Ausnahmehierarchie haben. Das ist ein bisschen altes Design, an dem ich arbeite, um es zu korrigieren (ich mache gute Fortschritte. Ich habe die selbstgewonnene String-Klasse und viele der hausgemachten Container losgeworden.)
UPDATE: Um es klar zu sagen, ich hatte den Fehler behoben (indem ich "throw e" in "throw" änderte), bevor ich die Frage überhaupt stellte. Ich wollte nur bestätigen, was vor sich geht.
Wenn Sie ein Objekt werfen, werfen Sie tatsächlich eine Kopie des Objekts, nicht das Original. Denken Sie darüber nach - das ursprüngliche Objekt befindet sich auf dem Stapel, aber der Stapel wird abgewickelt und ungültig gemacht.
Ich glaube, das ist ein Teil des Standards, aber ich habe keine Kopie als Referenz.
Der Typ der Ausnahme, die im catch-Block ausgelöst wird, ist der Basistyp des Caches und nicht der Typ des Objekts, das geworfen wurde. Das Problem liegt in throw;
und nicht in throw e;
, wodurch die ursprüngliche Ausnahme ausgelöst wird.
Ein kurzes Google schlägt vor, dass du ja den Kopierkonstruktor werfst, der benötigt wird und öffentlich sein muss. (Das macht Sinn, wenn Sie eine Kopie von e
initialisieren und diese werfen.)
Wie auch immer, benutze einfach throw
, ohne das Ausnahme-Objekt anzugeben, um das, was in catch
abgefangen wurde, erneut auszulösen. Sollte das das Problem nicht lösen?
Ja.
%Vor% löst eine Ausnahme des statischen -Typs von e
aus, unabhängig davon, was e
tatsächlich ist. In diesem Fall wird die Ausnahme Derived
mit einem Kopierkonstruktor in ein Exception
kopiert.
In diesem Fall können Sie einfach
%Vor%, um die Derived
-Ausnahmeblase korrekt zu erhalten.
Wenn Sie in anderen Fällen an polymorphem Werfen interessiert sind, beziehen Sie sich immer auf so nützlich C ++ FAQ Lite .
C ++ hört nie auf, mich zu überraschen. Ich hätte viel Geld verloren, wenn das eine Wette auf das Verhalten gewesen wäre!
Das Ausnahmeobjekt wird zuerst in ein temporäres Objekt kopiert und Sie sollten throw
verwenden. Um den Standard 15.1 / 3 zu zitieren:
Ein throw-Ausdruck initialisiert ein temporäres Objekt, das so genannte Exception-Objekt, dessen Typ bestimmt wird, indem alle cv-Qualifikationsmerkmale auf oberster Ebene vom statischen Typ des Operanden throw entfernt und der Typ von "array of T" angepasst wird "oder" -Funktion, die T "zu" Zeiger zu T "bzw." Zeiger zu Funktion, die T zurückgibt "zurückgibt.
Ich denke, das führt zu einer sehr nützlichen Kodierungsstandardregel:
Basisklassen einer Ausnahmehierarchie sollten einen reinen virtuellen Destruktor haben.
oder
Der Kopierkonstruktor für eine Basisklasse in einer Ausnahmehierarchie soll geschützt sein.
Entweder wird das Ziel erreicht, dass der Compiler warnt, wenn Sie versuchen, "e" zu werfen, da Sie im ersten Fall keine Instanz einer abstrakten Klasse erstellen können und die zweite, weil Sie den Kopierkonstruktor nicht aufrufen können.
Tags und Links c++ exception object-slicing