Cómo lanzar una excepción de C ++

260

Entiendo muy mal el manejo de excepciones (es decir, cómo personalizar las declaraciones de lanzamiento, intento y captura para mis propios fines).

Por ejemplo, he definido una función de la siguiente manera: int compare(int a, int b){...}

Me gustaría que la función arroje una excepción con algún mensaje cuando ao b sea negativo.

¿Cómo debería abordar esto en la definición de la función?

Terry Li
fuente
3
Debería leer esto: gotw.ca/publications/mill22.htm .
Oliver Charlesworth
37
@OliCharlesworth, ¿no crees que eso es demasiado para arrojar a alguien que está confundido por lo básico?
Mark Ransom
66
Vale la pena evitar las excepciones superfluas. Si no desea que su interlocutor pase valores negativos, hágalo más obvio especificando unsigned intlos parámetros en la firma de su función. Por otra parte, soy de la escuela en la que solo debes lanzar y atrapar excepciones para cosas que son realmente excepcionales.
AJG85
1
@ Mark: Originalmente entendí mal la pregunta sobre si uno debería usar throw()especificaciones de excepción en las funciones.
Oliver Charlesworth

Respuestas:

364

Sencillo:

#include <stdexcept>

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

La Biblioteca estándar viene con una buena colección de objetos de excepción incorporados que puede lanzar. Tenga en cuenta que siempre debe arrojar por valor y capturar por referencia:

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

Puede tener varias instrucciones catch () después de cada intento, por lo que puede manejar diferentes tipos de excepción por separado si lo desea.

También puede volver a lanzar excepciones:

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

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

Y para detectar excepciones independientemente del tipo:

catch( ... ) { };
nsanders
fuente
26
Y siempre debe atrapar excepciones como constante
Adrian Cornish
2
@TerryLiYifeng si las excepciones personalizadas tienen más sentido, entonces adelante. Es posible que aún desee derivar de std :: exception y mantener la interfaz igual.
nsanders
2
Volví a hacer +1 pero creo que es bastante importante, porque resalta el hecho de que ahora es un objeto temporal, por lo que la modificación es inútil.
Adrian Cornish
2
@AdrianCornish: Sin embargo, no es realmente temporal. Las capturas no constantes pueden ser útiles .
GManNickG
26
Por lo general, volvería a lanzar con un simple throw;( volver a lanzar el objeto original y preservar su tipo) en lugar de throw e;(arrojar una copia del objeto capturado, posiblemente cambiando su tipo).
Mike Seymour
17

Simplemente agregue throwdonde sea necesario y trybloquee a la persona que llama que maneja el error. Por convención, solo debe arrojar cosas que se derivan std::exception, por lo tanto, incluya <stdexcept>primero.

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) {
        // ...
    }
}

Además, busque en Boost.Exception .

Cat Plus Plus
fuente
15

Aunque esta pregunta es bastante antigua y ya ha sido respondida, solo quiero agregar una nota sobre cómo hacer un manejo adecuado de excepciones en C ++ 11:

Uso std::nested_exceptionystd::throw_with_nested

Se describe en StackOverflow aquí y aquí , cómo puede obtener un seguimiento de sus excepciones dentro de su código sin necesidad de un depurador o un registro engorroso, simplemente escribiendo un controlador de excepciones adecuado que arroje excepciones anidadas.

Como puede hacer esto con cualquier clase de excepción derivada, ¡puede agregar mucha información a dicha traza inversa! También puede echar un vistazo a mi MWE en GitHub , donde una traza inversa se vería así:

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"
GPMueller
fuente
8

Puede definir un mensaje para lanzar cuando se produce un cierto error:

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

o podrías definirlo así:

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

Por lo general, tendría un try ... catchbloque como este:

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

Quería AGREGAR a las otras respuestas descritas aquí una nota adicional, en el caso de excepciones personalizadas .

En el caso de que cree su propia excepción personalizada, que se deriva de std::exception, cuando captura "todos los tipos de excepciones posibles", siempre debe comenzar las catchcláusulas con el tipo de excepción "más derivado" que se puede detectar. Vea el ejemplo (de lo que NO debe hacer):

#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;
}

NOTA:

0) El orden correcto debe ser al revés, es decir, primero usted, catch (const MyException& e)seguido de catch (const std::exception& e).

1) Como puede ver, cuando ejecuta el programa tal como está, se ejecutará la primera cláusula catch (que probablemente es lo que NO quería en primer lugar).

2) Aunque el tipo capturado en la primera cláusula catch es de tipo std::exception, what()se llamará a la versión "adecuada" , porque se captura por referencia (cambie al menos el std::exceptiontipo de argumento capturado por valor) y experimentará el fenómenos de "corte de objetos" en acción).

3) En caso de que "algún código debido al hecho de que se lanzó la excepción XXX ..." hace cosas importantes CON RESPECTO al tipo de excepción, hay un mal comportamiento de su código aquí.

4) Esto también es relevante si los objetos capturados eran objetos "normales" como: class Base{};y class Derived : public Base {}...

5) g++ 7.3.0en Ubuntu 18.04.1 produce una advertencia que indica el problema mencionado:

En la función 'void illustrateDerivedExceptionCatch ()': item12Linux.cpp: 48: 2: advertencia: se capturará la excepción del tipo 'MyException' (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: advertencia: por el controlador anterior para 'std :: excepción' catch (const except & e) ^ ~~~~

Una vez más , diré que esta respuesta es solo para AGREGAR a las otras respuestas descritas aquí (pensé que vale la pena mencionar este punto, pero no pude describirlo en un comentario).

Guy Avraham
fuente