Как бросить исключение C ++ - PullRequest
202 голосов
/ 13 декабря 2011

У меня очень плохое понимание обработки исключений (т. Е. Как настроить операторы throw, try, catch для моих собственных целей).

Например, я определил функцию следующим образом: int compare(int a, int b){...}

Я бы хотел, чтобы функция генерировала исключение с некоторым сообщением, когда a или b отрицательны.

Как мне подойти к этому в определении функции?

Ответы [ 5 ]

292 голосов
/ 13 декабря 2011

Simple:

#include <stdexcept>

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

Стандартная библиотека поставляется с хорошей коллекцией встроенных исключительных объектов , которые вы можете выбросить. Имейте в виду, что вы всегда должны бросать по значению и ловить по ссылке:

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

Вы можете иметь несколько операторов catch () после каждой попытки, так что вы можете обрабатывать разные типы исключений отдельно, если хотите.

Вы также можете повторно выдать исключения:

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

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

И для отлова исключений независимо от типа:

catch( ... ) { };
16 голосов
/ 13 декабря 2011

Просто добавьте throw, где это необходимо, и блок try к вызывающей стороне, которая обрабатывает ошибку.По соглашению вы должны бросать только то, что происходит от std::exception, поэтому сначала включите <stdexcept>.

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

Также посмотрите на Boost.Exception .

12 голосов
/ 26 октября 2017

Хотя этот вопрос довольно старый и на него уже дан ответ, я просто хочу добавить примечание о том, как правильно обрабатывать исключения в C ++ 11:

Использовать std::nested_exception и std::throw_with_nested

Это описывается в StackOverflow здесь и здесь , как вы можете получить обратную трассировку для ваших исключений внутри вашего кода без необходимости в отладчике или громоздком ведении журнала, просто написав правильный обработчик исключений, который будет перебрасывать вложенные исключения.

Поскольку вы можете сделать это с любым производным классом исключений, вы можете добавить многоинформации к такому следу!Вы также можете взглянуть на мой MWE на GitHub , где обратная трассировка будет выглядеть примерно так:

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"
6 голосов
/ 02 февраля 2017

Вы можете определить сообщение, которое будет выдано при возникновении определенной ошибки:

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

или вы можете определить это так:

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

Как правило, у вас будет блок try ... catch, например:

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }
3 голосов
/ 19 сентября 2018

Требуется ДОБАВИТЬ к другим описанным здесь дополнительным примечаниям, в случае пользовательских исключений .

В случае, когда вы создаете свое собственное пользовательское исключение, которое происходит от std::exception, когда вы перехватываете «все возможные» типы исключений, вы всегда должны начинать предложения catch с «самым производным» исключениемТип, который может быть пойман.См. Пример (из того, что НЕ сделать):

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

ПРИМЕЧАНИЕ:

0) Правильный порядок должен быть наоборот, т. е. сначала вы catch (const MyException& e), за которым следует catch (const std::exception& e).

1) Как вы можете видеть, когда вы запустите программу как есть, будет выполнено первое предложение catch (что, вероятно, то, что выдействительно, NOT требуется в первую очередь).

2) Даже если тип, перехваченный в первом предложении catch, относится к типу std::exception, «правильная» версия what() будетбыть вызванным - потому что он перехватывается по ссылке (измените, по крайней мере, тип перехваченного аргумента std::exception на значение - и вы увидите явление "разрезания объекта" в действии).

3) В случае, если«некоторый код из-за того, что было сгенерировано исключение XXX ...» делает важные вещи WITH RESPECT для типа исключения, здесь присутствует неправильное поведение вашего кода.

4) Это также актуально, если перехваченообъекты были "нормальными" объектами, такими как: class Base{}; и class Derived : public Base {} ...

5) g++ 7.3.0 в Ubuntu 18.04.1 выдает предупреждение, указывающее на упомянутую проблему:

В функции 'void illustrateDerivedExceptionCatch ()': item12Linux.cpp: 48: 2: предупреждение: исключение типа 'MyException ' будет пойман catch (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: предупреждение: от более раннего обработчика для' std:: исключение ' catch (const exception & e) ^ ~~~~

Снова , я скажу, что этот ответ только на ДОБАВЬТЕ к другим ответам, описанным здесь (я думал, что этот момент стоит упомянуть, но не смог изобразить его в комментарии).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...