Все исключения в языке C++ описываются типом exception, который определен в заголовочном файле <exception>. И при обработке исключений мы также можем использовать данный класс, интерфейс которого выглядит следующим образом
namespace std { class exception { public: exception() noexcept; exception(const exception&) noexcept; exception& operator=(const exception&) noexcept; virtual ~exception(); // Destructor virtual const char* what() const noexcept; // возвращает сообщение об исключении }; }
Используем данный тип для обработки исключения:
#include <iostream> double divide(int, int); int main() { int x {500}; int y{}; try { double z = divide(x, y); std::cout << z << std::endl; } catch (const std::exception& err) { std::cout << "Error!!!" << std::endl; } std::cout << "The End..." << std::endl; } double divide(int a, int b) { if (!b) throw std::exception(); return a / b; }
Прежде всего, оператору throw передается объект типа std::exception
throw std::exception();
Если мы хотим отловить исключения типа exception, то нам надо в выражении catch определить переменную этого типа:
catch (const std::exception& err)
То есть здесь err представляет константную ссылку на объект exception. Если мы не собираемся использовать эту переменную в блоке catch, то можно указать просто тип исключения:
catch (std::exception) { std::cout << "Error!!!" << std::endl; }
С помощью функции what() можно получить сообщение об ошибке в виде строки в С-стиле. Однако непосредственно для типа std::exception он имеет мало смысла, поскольку просто выводит название класса. Но в производных классах он может использоваться для вывода сообщения об ошибке.
На основе класса std::exception
мы можем создавать свои собственные типы исключений. Например:
#include <iostream> class person_error: public std::exception { public: person_error(const std::string& message): message{message} {} const char* what() const noexcept override { return message.c_str(); // получаем из std::string строку const char* } private: std::string message; // сообщение об ошибке }; class Person { public: Person(std::string name, unsigned age) { if(!age || age > 110) // если age==0 или age > 110 throw person_error("Invalid age"); this->name = name; this->age = age; } void print() const { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } private: std::string name; unsigned age; }; void testPerson(std::string name, unsigned age) { try { Person person{name, age}; // создаем один объект Person person.print(); } catch (const person_error& err) // обработка ошибок, сязанных с Person { std::cout << "Person error: " << err.what() << std::endl; } catch (const std::exception&) // обработка остальных исключений { std::cout << "Something wrong"<< std::endl; } } int main() { testPerson("Tom", 38); // Name: Tom Age: 38 testPerson("Sam", 250); // Person error: Invalid age }
Здесь определен класс Person, который представляет пользователя. В конструктор класса передается имя и возраст. Однако передаваемое число может превышать разумный возраст или быть равно нулю. В этом случае мы генерируем
исключение типа person_error
:
Person(std::string name, unsigned age) { if(!age || age > 110) // если age==0 или age > 110 throw person_error("Invalid age");
Класс person_error унаследован от std::exception, через конструктор получает сообщение об ошибке и хранит его в переменной message:
class person_error: public std::exception { public: person_error(const std::string& message): message{message} {} const char* what() const noexcept override { return message.c_str(); // получаем из std::string строку const char* } private: std::string message; // сообщение об ошибке };
Для возвращения сообщения нам надо переопределить виртуальную функцию what()
. Но проблема заключается в том, что функция возвращает строку const char*
,
но класс хранит сообщение в виде строки std::string. И чтобы получить из std::string
значение const char*
, у строки вызываем функцию c_str()
return message.c_str(); // получаем из std::string строку const char*
Для теста определена функция testPerson, в которой в блоке try создается объект Person. Конструкция try..catch использует два блока catch для обработки исключений. Первый блок обрабатывает исключения производного типа - класса person_error, а последний блок представляет базовый тип exceptionЖ
catch (const person_error& err) // обработка ошибок, сязанных с Person { std::cout << "Person error: " << err.what() << std::endl; } catch (const std::exception&) // обработка остальных исключений { std::cout << "Something wrong"<< std::endl; }
И в данном случае программа выдаст следующий результат:
Name: Tom Age: 38 Person error: Invalid age