394 Stimmen

Wie man eine C++-Ausnahme auslöst

Ich habe ein sehr schlechtes Verständnis der Ausnahmebehandlung (d.h., wie man throw, try, catch-Anweisungen für meine eigenen Zwecke anpassen).

Ich habe zum Beispiel eine Funktion wie folgt definiert: int compare(int a, int b){...}

Ich möchte, dass die Funktion eine Ausnahme mit einer Meldung auslöst, wenn entweder a oder b negativ ist.

Wie sollte ich dies bei der Definition der Funktion berücksichtigen?

522voto

nsanders Punkte 11482

Einfach:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

Die Standardbibliothek kommt mit einer schönen Sammlung von eingebaute Ausnahmeobjekte können Sie werfen. Denken Sie daran, dass Sie immer per Wert werfen und per Referenz fangen sollten:

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

Nach jedem Try können Sie mehrere catch()-Anweisungen einfügen, so dass Sie verschiedene Ausnahmetypen getrennt behandeln können, wenn Sie möchten.

Sie können auch Ausnahmen erneut auslösen:

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

Und um Ausnahmen unabhängig vom Typ abzufangen:

catch( ... ) { };

26voto

GPMueller Punkte 2377

Obwohl diese Frage ziemlich alt ist und bereits beantwortet wurde, möchte ich nur einen Hinweis darauf geben, wie man eine korrekte Ausnahmebehandlung in C++11 durchführt:

Verwenden Sie std::nested_exception y std::throw_with_nested

Es wird auf StackOverflow beschrieben aquí y aquí , wie Sie eine Rückverfolgung der Ausnahmen zu erhalten innerhalb Ihres Codes, ohne dass ein Debugger oder eine umständliche Protokollierung erforderlich ist, indem Sie einfach einen geeigneten Exception-Handler schreiben, der verschachtelte Ausnahmen auslöst.

Da Sie dies mit jeder abgeleiteten Ausnahmeklasse tun können, können Sie einer solchen Rückverfolgung eine Menge Informationen hinzufügen! Sie können auch einen Blick auf meine MWE auf GitHub , wobei ein Backtrace etwa so aussehen würde:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

21voto

Cat Plus Plus Punkte 119072

Einfach hinzufügen throw wo nötig, und try Block an den Aufrufer, der den Fehler behandelt. Nach der Konvention sollten Sie nur Dinge werfen, die sich von std::exception also auch <stdexcept> Erstens.

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

Informieren Sie sich auch über Boost.Exception .

12voto

serup Punkte 3258

Sie können eine Meldung definieren, die beim Auftreten eines bestimmten Fehlers ausgegeben wird:

throw std::invalid_argument( "received negative value" );

oder Sie könnten es so definieren:

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

Normalerweise würden Sie eine try ... catch so blockieren:

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }

12voto

Guy Avraham Punkte 3086

Ich füge diese Antwort hinzu, da es nicht vorteilhaft erscheint, zu diesem Zeitpunkt eine weitere Antwort für diese Frage und Antwort zu erstellen.

In dem Fall, dass Sie Ihre eigene benutzerdefinierte Ausnahme erstellen, die sich von std::exception Wenn Sie "alle möglichen" Ausnahmetypen abfangen, sollten Sie immer mit der catch Klauseln mit dem "am meisten abgeleiteten" Ausnahmetyp, der abgefangen werden kann. Siehe das Beispiel (für das, was NICHT zu tun):

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException::what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

HINWEIS:

  1. Die richtige Reihenfolge sollte umgekehrt sein, d. h., zuerst müssen Sie catch (const MyException& e) gefolgt von catch (const std::exception& e) .

  2. Wie Sie sehen können, wird die erste Catch-Klausel ausgeführt, wenn Sie das Programm so ausführen, wie es ist (was Sie wahrscheinlich auch getan haben NICHT in erster Linie wollen).

  3. Auch wenn der in der ersten catch-Klausel gefangene Typ vom Typ std::exception , die "richtige" Version von what() aufgerufen wird - weil es durch eine Referenz abgefangen wird (ändern Sie zumindest das Argument caught std::exception und Sie werden das Phänomen des "Object Slicing" in Aktion erleben).

  4. Für den Fall, dass der "einige Code aufgrund der Tatsache, dass XXX Ausnahme ausgelöst wurde..." wichtige Dinge MIT BEZUG auf den Ausnahmetyp tut, gibt es Fehlverhalten von Ihrem Code hier.

  5. Dies gilt auch, wenn es sich bei den gefangenen Objekten um "normale" Objekte handelt: class Base{}; y class Derived : public Base {} ...

  6. g++ 7.3.0 auf Ubuntu 18.04.1 erzeugt eine Warnung, die auf das genannte Problem hinweist:

In der Funktion 'void illustrateDerivedExceptionCatch()': item12Linux.cpp:48:2: Warnung: Ausnahme vom Typ 'MyException' wird gefangen werden catch(const MyException& e) ^~~~~

item12Linux.cpp:43:2: Warnung: durch einen früheren Handler für 'std::exception' catch (const exception& e) ^~~~~

Nochmals Ich möchte darauf hinweisen, dass diese Antwort nur für ADD zu den anderen hier beschriebenen Antworten (ich fand diesen Punkt erwähnenswert, konnte ihn aber nicht in einem Kommentar darstellen).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X