Создание пользовательских исключений в C++
Я изучаю C++ и сталкиваюсь с проблемой, когда пытаюсь создать собственное исключение и выбросить его в Linux.
Я создал небольшой тестовый проект для проверки своей реализации, и ниже приведен заголовочный файл моего класса исключения.
class TestClass : public std::runtime_error
{
public:
TestClass(char const* const message) throw();
virtual char const* what() const throw();
};
Исходный файл класса исключения выглядит следующим образом:
using namespace std;
TestClass::TestClass(char const* const message) throw()
: std::runtime_error(message)
{
}
char const * TestClass::what() const throw()
{
return exception::what();
}
В моем основном приложении я вызываю функцию, которая выбрасывает мое исключение, и перехватываю его в блоке try/catch следующим образом:
void runAFunctionAndthrow();
/*
*
*/
int main(int argc, char** argv) {
try
{
cout << "About to call function" << endl;
runAFunctionAndthrow();
}
catch (TestClass ex)
{
cout << "Exception Caught: " << ex.what() << endl;
}
return 0;
}
void runAFunctionAndthrow()
{
cout << "going to run now. oh dear I need to throw an exception" << endl;
stringstream logstream;
logstream << "This is my exception error. :(";
throw TestClass(logstream.str().c_str());
}
Когда я запускаю программу, я ожидаю получить следующий вывод:
About to call function
going to run now. oh dear I need to throw an exception
Exception Caught: This is my exception error. :(
Однако вместо этого я получаю:
About to call function
going to run now. oh dear I need to throw an exception
Exception Caught: std::exception
Обратите внимание на последнюю строку: вместо моего фактического сообщения об исключении "This is my exception error. 😦" выводится std::exception.
Почему так происходит? На Windows все работает нормально, а на Linux — нет.
Судя по различным постам, я сделал все правильно, так что что же я упустил?
2 ответ(ов)
Первое, большинство информации о данном вопросе уже было предоставлено Самом Варшавчиком. Однако я хотел бы добавить одно важное замечание. При выбросе и перехвате исключений хорошо следовать правилу:
"Выбрасывай по значению, перехватывай по ссылке"
Ваш код для выброса исключения выглядит корректно:
void runAFunctionAndthrow()
{
cout << "Сейчас буду выполняться. Ой, мне нужно выбросить исключение" << endl;
stringstream logstream;
logstream << "Это мое исключение с ошибкой. :(";
throw TestClass(logstream.str().c_str());
}
Здесь используется неявное преобразование к TestClass
, после чего объект передается по значению.
Ключевой момент этого правила заключается в минимизации выделения памяти и обработки между различными стековыми рамками.
Однако ваш обработчик исключения не следует этому правилу, так как вы перехватываете исключение по значению:
catch (TestClass ex)
{
cout << "Исключение перехвачено: " << ex.what() << endl;
}
Правильный вариант перехвата должен выглядеть так: (const TestClass& ex).
Ключевым моментом этого правила является неявное преобразование между базовым и производным классами.
Самый краткий способ создать пользовательское исключение начиная с C++11 — это использовать конструктор родителя std::runtime_error
:
struct my_exception : std::runtime_error {
using std::runtime_error::runtime_error;
};
Это позволит унаследовать все конструкторы std::runtime_error
, что покрывает большинство потребностей в обработке исключений.
Когда действительно стоит использовать noexcept?
Возможно ли вывести тип переменной в стандартном C++?
Неопределенная ссылка на виртуальную таблицу (vtable)
Статические глобальные переменные в C++
Простой способ найти неинициализированные переменные-члены