2010-01-07 10 views
42

Ich fand, dass es drei Möglichkeiten gibt, eine Ausnahme zu fangen, was sind die Unterschiede?fangen Ausnahme durch Zeiger in C++

1) Fang nach Wert;

2) Fang durch Referenz;

3) Fang per Zeiger;

Ich weiß nur, dass catch nach Wert wird zwei Kopien des Objekts aufrufen, fangen durch Verweis wird eine aufrufen. Also, wie wäre es mit Zeiger? Wann Fang durch Zeiger verwenden? Kann ich neben einem Objekt auch einen Zeiger auf ein Objekt wie dieses werfen?

class A {} 

void f() { 

    A *p = new A(); 
     throw p; 


} 
+6

Sie kann __not__ keine Ausnahme durch Zeiger abfangen. Sie können eine Ausnahme abfangen, bei der es sich um einen Zeiger handelt. Das Problem ist, dass A und A * zwei völlig verschiedene Typen sind. Wenn Sie einen Zeiger auf A werfen, können Sie nur Werte oder Referenzen erfassen. Aber es ist ein A *, das Sie durch Wert oder Referenz nicht die A. fangen. –

Antwort

70

Der empfohlene Weg ist von Wert und fangen durch Bezugnahme zu werfen.

Ihr Beispielcode löst einen Zeiger aus, was eine schlechte Idee ist, da Sie den Speicher an der Fangstelle verwalten müssen.

Wenn Sie wirklich glauben, dass Sie einen Zeiger werfen sollten, verwenden Sie einen intelligenten Zeiger wie shared_ptr.

Wie auch immer, Herb Sutter und Alexei Alexandrescu erklären das wirklich gut in ihrem C++ Coding Standards Buch, das ich paraphrasierte.

Siehe C++ Coding Standards: Throw by Value, Catch by Reference.

+12

Und wenn der Grund, den Sie werfen, ist, weil Sie nicht mehr im Speicher sind, dann wird der Versuch, ein neues Objekt zu verteilen, nicht helfen. –

+3

Sie würden entweder einen Zeiger auf A werfen, oder Sie würden std :: bad_alloc werfen, je nachdem, ob das A zugewiesen werden könnte. Also würdest du wenigstens etwas werfen ... –

+0

Ich habe Code wie 'const std :: runtime_error err; throw err; 'bin ich richtig, wenn ich annehme, dass dies nicht durch Referenz abgefangen wird (keine Konvertierung von' const std :: runtime_error & 'nach' std :: runtime_error & ')? Dies wird daher von der Laufzeit abgefangen und stürzt vermutlich das Programm ab. Habe ich recht? –

1

Es gibt nicht wirklich ein gutes Szenario zum Abfangen/Werfen einer Ausnahme durch Zeiger. C++ Semantik erlaubt es, aber es ist nicht sehr nützlich, da Sie die meiste Zeit eine temporäre Ausnahme oder String-Objekt werfen werden.

Allerdings verwenden einige Bibliotheken (Boost.Graph tut dies, glaube ich) mit throw einen Rückgabewert zurück an den Aufrufer von einer tief rekursierten Funktion; In einer Situation wie dieser kann der Rückgabewert ein Zeiger sein, so dass das Werfen eines Zeigers Sinn machen würde.

14

Catch folgt normalen Zuweisungskompatibilitätsregeln, das heißt, wenn Sie einen Wert werfen, können Sie ihn als Wert oder Verweis fangen, aber nicht als Zeiger; Wenn Sie einen Zeiger werfen, können Sie ihn nur als Zeiger (oder Verweis auf einen Zeiger ...) fangen.

Aber es macht nicht wirklich Sinn, Zeiger zu werfen, es wird nur Speichermanagement Kopfschmerzen verursachen. Daher sollten Sie im Allgemeinen die Regel Wurf nach Wert, durch Referenz, wie von Gregory erläutert folgen.

4

Microsoft MFC verwendet catch by pointer, aber ich denke, das war für die Kompatibilität mit dem Compiler vor Versuch und Catch wurden ordnungsgemäß implementiert; Ursprünglich verwendeten sie TRY- und CATCH-Makros, um es zu simulieren. Jede Ausnahme wird von CException abgeleitet, die eine Methode aufweist, um zu bestimmen, ob das Objekt gelöscht werden muss.

Ich würde das für keine moderne Ausnahme empfehlen. Fang durch Referenz ist der Weg zu gehen.

2

Während es möglich ist, im Wesentlichen jedes Objekt eines beliebigen Typs zu werfen, gibt es wenig (wenn überhaupt) zu gewinnen, indem man dies tut. Die dynamische Zuweisung ist in erster Linie dann nützlich, wenn ein Objekt eine Lebensdauer haben muss, die nicht mit der automatischen Zuweisung übereinstimmt - d. H. Sie möchten, dass seine Lebensdauer unabhängig vom normalen Programmumfang ist.

Im Falle eines Ausnahmeobjekts macht das jedoch nicht wirklich viel Sinn. Ein Ausnahmeobjekt wird normalerweise nur innerhalb eines Ausnahmebehandlers verwendet, und Sie möchten eindeutig, dass es zerstört wird, wenn Sie den (letzten) Handler für diese Ausnahme beenden.

Es gibt auch die Tatsache, dass Sie im Allgemeinen Ausnahmebehandlungscode ziemlich einfach halten wollen. Wenn Sie beispielsweise versuchen, den freien Speicher/Heap zu melden, der erschöpft oder beschädigt ist, funktioniert das Ausschließen des Ausnahmeobjekts aus diesem erschöpften/beschädigten Free-Speicher/Heap normalerweise nicht sehr gut ...