Все исключения в языке 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>
#include <string>
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