C ++ исключение: бросая std :: string - PullRequest
72 голосов
/ 25 сентября 2008

Я хотел бы вызвать исключение, когда мои методы C ++ сталкиваются с чем-то странным и не могут восстановиться. Можно ли бросить указатель std::string?

Вот что я с нетерпением ждал, чтобы сделать:

void Foo::Bar() {
    if(!QueryPerformanceTimer(&m_baz)) {
        throw new std::string("it's the end of the world!");
    }
}

void Foo::Caller() {
    try {
        this->Bar(); // should throw
    }
    catch(std::string *caught) { // not quite sure the syntax is OK here...
        std::cout << "Got " << caught << std::endl;
    }
}

Ответы [ 7 ]

96 голосов
/ 25 сентября 2008

Да. std::exception - базовый класс исключений в стандартной библиотеке C ++. Возможно, вы захотите избегать использования строк в качестве классов исключений, поскольку они сами могут генерировать исключения во время использования. Если это произойдет, то где ты будешь?

boost имеет отличный документ с хорошим стилем для исключений и обработки ошибок. Это стоит прочитать.

58 голосов
/ 25 сентября 2008

Несколько принципов:

  1. у вас есть базовый класс std :: exception, у вас должны быть свои исключения из него. Таким образом, общий обработчик исключений все еще имеет некоторую информацию.

  2. Не бросайте указатели, а возражайте, так как память обрабатывается для вас.

Пример:

struct MyException : public std::exception
{
   std::string s;
   MyException(std::string ss) : s(ss) {}
   ~MyException() throw () {} // Updated
   const char* what() const throw() { return s.c_str(); }
};

А затем используйте его в своем коде:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw MyException("it's the end of the world!");
  }
}

void Foo::Caller(){
  try{
    this->Bar();// should throw
  }catch(MyException& caught){
    std::cout<<"Got "<<caught.what()<<std::endl;
  }
}
21 голосов
/ 25 сентября 2008

Все эти работы:

#include <iostream>
using namespace std;

//Good, because manual memory management isn't needed and this uses
//less heap memory (or no heap memory) so this is safer if
//used in a low memory situation
void f() { throw string("foo"); }

//Valid, but avoid manual memory management if there's no reason to use it
void g() { throw new string("foo"); }

//Best.  Just a pointer to a string literal, so no allocation is needed,
//saving on cleanup, and removing a chance for an allocation to fail.
void h() { throw "foo"; }

int main() {
  try { f(); } catch (string s) { cout << s << endl; }
  try { g(); } catch (string* s) { cout << *s << endl; delete s; }
  try { h(); } catch (const char* s) { cout << s << endl; }
  return 0;
}

Вы должны предпочесть h, а не g. Обратите внимание, что в наименее предпочтительном варианте вам необходимо явно освободить память.

7 голосов
/ 25 сентября 2008

В дополнение к возможному выбрасыванию чего-то, полученного из std :: exception, вы должны бросить анонимные временные и ловить по ссылке:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw std::string("it's the end of the world!");
  }
}

void Foo:Caller(){
  try{
    this->Bar();// should throw
  }catch(std::string& caught){ // not quite sure the syntax is ok here...
    std::cout<<"Got "<<caught<<std::endl;
  }
}
  • Вы должны бросить анонимный временные, поэтому компилятор имеет дело с продолжительностью жизни объекта любого вы бросаете - если вы бросаете что-то новенькое из кучи, кто-то должен освободить вещь.
  • Вы должны ловить ссылки на предотвратить нарезку объектов

.

См. Подробности Мейера "Effective C ++ - 3-е издание" или посетите https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+reference

7 голосов
/ 25 сентября 2008

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

Кстати, бросание std::string - не лучший подход для начала. У вас будет намного больше гибкости, если вы будете использовать простой объект-обертку. Он может просто инкапсулировать string на данный момент, но, возможно, в будущем вы захотите включить другую информацию, например, некоторые данные, которые вызвали исключение, или, возможно, номер строки (очень часто это). Вы не хотите менять всю обработку исключений в каждой точке вашей кодовой базы, поэтому следуйте по пути и не выбрасывайте необработанные объекты.

4 голосов
/ 11 июля 2013

Самый простой способ вызвать исключение в C ++:

#include <iostream>
using namespace std;
void purturb(){
    throw "Cannot purturb at this time.";
}
int main() {
    try{
        purturb();
    }
    catch(const char* msg){
        cout << "We caught a message: " << msg << endl;
    }
    cout << "done";
    return 0;
}

Это печатает:

We caught a message: Cannot purturb at this time.
done

Если вы поймаете выброшенное исключение, оно будет сохранено, и программа продолжит работу. Если вы не поймали исключение, программа существует и выдает:

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

1 голос
/ 03 декабря 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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...