Was ist der Zweck einer Funktion try Block? [Duplikat]

8

Dieser Code löst beim Erstellen des int -Objekts in der Klasse Dog eine UseResources -Ausnahme aus. Die int -Ausnahme wird von einem normalen try-catch Block abgefangen und der Code gibt aus:

%Vor%

%Vor%

Wenn wir nun die Definition des UseResources() -Konstruktors durch eine mit function try block ersetzen, wie unten,

%Vor%

Die Ausgabe ist die gleiche

%Vor%

, d.h. mit genau dem gleichen Endergebnis.

Was ist dann der Zweck von function try block ?

    
Belloc 02.12.2011, 16:43
quelle

2 Antworten

9

Stellen Sie sich vor, dass UseResources wie folgt definiert wurde:

%Vor%

Wenn Dog::Dog() auslöst, wird cat Speicher verlieren. Becase UseResources 's Konstruktor nie abgeschlossen , das Objekt wurde nie vollständig erstellt. Daher wird der Destruktor nicht aufgerufen.

Um dieses Leck zu verhindern, müssen Sie einen try / catch-Block auf Funktionsebene verwenden:

%Vor%

Um Ihre Frage vollständiger zu beantworten, ist der Zweck eines try / catch-Blocks auf Funktionsebene in Konstruktoren speziell, diese Art der Bereinigung durchzuführen. Try / catch-Blöcke auf Funktionsebene können keine Ausnahmen verschlingen (normale können das). Wenn sie etwas fangen, werfen sie es erneut, wenn sie das Ende des Catch-Blocks erreichen, es sei denn, Sie wiederholen es explizit mit throw . Sie können eine Art von Ausnahme in eine andere umwandeln, aber Sie können es nicht einfach schlucken und weitermachen, als wäre es nicht passiert.

Dies ist ein weiterer Grund, warum Werte und intelligente Zeiger anstelle von nackten Zeigern verwendet werden sollten, sogar als Klassenmitglieder. Denn wie in Ihrem Fall, wenn Sie nur Memberwerte anstelle von Zeigern haben, müssen Sie dies nicht tun. Es ist die Verwendung eines nackten Zeigers (oder einer anderen Form von Ressource, die nicht in einem RAII-Objekt verwaltet wird), die solche Dinge erzwingt.

Beachten Sie, dass dies die einzige legitime Verwendung von Funktion try / catch-Blöcken ist.

Weitere Gründe, die Funktion try blocks nicht zu verwenden. Der obige Code ist subtil gebrochen. Bedenken Sie Folgendes:

%Vor%

Was passiert also im Konstruktor von UseResources ? Nun, der Ausdruck new Cat wird offensichtlich werfen. Aber das bedeutet, dass cat niemals initialisiert wurde. Das bedeutet, dass delete cat zu undefiniertem Verhalten führt.

Sie können versuchen, dies zu korrigieren, indem Sie ein komplexes Lambda statt nur new Cat verwenden:

%Vor%

Das behebt das Problem theoretisch, bricht jedoch eine angenommene Invariante von UseResources . Das heißt, dass UseResources::cat immer ein gültiger Zeiger ist. Wenn das tatsächlich eine Invariante von UseResources ist, dann wird dieser Code fehlschlagen, weil er die Konstruktion von UseResources trotz der Ausnahme erlaubt.

Grundsätzlich ist es nicht möglich, diesen Code sicher zu machen, es sei denn new Cat ist noexcept (entweder explizit oder implizit).

Im Gegensatz dazu funktioniert das immer:

%Vor%

Betrachten Sie kurz einen Try-Block auf Funktionsebene als ernsten Code-Geruch.

    
Nicol Bolas 02.12.2011, 16:50
quelle
2

Ordentliche Funktion Try Blöcke haben relativ wenig Zweck. Sie sind fast identisch mit einem Versuchsblock im Körper:

%Vor%

Der einzige Unterschied besteht darin, dass die Funktion try-block im etwas größeren Funktionsumfang lebt, während die zweite Konstruktion im Funktion-body-scope lebt. Der erstere Bereich sieht nur die Funktionsargumente, letzterer auch lokal Variablen (dies betrifft jedoch nicht die beiden Versionen von try-Blöcken).

Die einzige interessante Anwendung kommt in einem -Konstruktor -try-block:

%Vor%

Nur so können Ausnahmen von einem der Initialisierer abgefangen werden. Sie können die Ausnahme nicht verarbeiten , da die gesamte Objektkonstruktion noch fehlschlagen muss (daher müssen Sie den catch-Block mit einer Ausnahme beenden, ob Sie wollen oder nicht). Es ist jedoch die einzige Möglichkeit, Ausnahmen von der Initialisierungsliste speziell zu behandeln.

Ist das nützlich? Wahrscheinlich nicht. Es gibt im Wesentlichen keinen Unterschied zwischen einem Konstruktor-try-Block und dem folgenden typischen "initialize-to-null-and-assign" -Muster, das selbst furchtbar ist:

%Vor%

Wie Sie sehen können, haben Sie ein unüberwindliches, unüberwindliches Durcheinander. Ein Konstruktor-try-Block wäre noch schlechter, weil man nicht einmal sagen konnte, wie viele der Zeiger bereits vergeben sind. Also ist es wirklich nur nützlich, wenn Sie genau zwei leakable Zuweisungen haben. Update: Danke dem Lesen von diese Frage Ich wurde darauf aufmerksam gemacht, dass Sie den catch-Block nicht benutzen können, um Ressourcen zu bereinigen , da der Verweis auf Member-Objekte nicht definiert ist. Also [Update beenden]

Kurz gesagt: Es ist nutzlos.

    
Kerrek SB 02.12.2011 17:11
quelle