Tipul exception
Toate excepțiile din limbajul C++ sunt descrise prin tipul exception, care este definit în fișierul header <exception>. Și la tratarea excepțiilor putem folosi această clasă, a cărei interfață arată astfel:
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; // returnează mesajul erorii
};
}
Să folosim acest tip pentru a trata o excepție:
#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;
}
În primul rând, operatorul throw primește un obiect de tip std::exception:
throw std::exception();
Dacă vrem să prindem excepții de tipul exception, atunci trebuie ca în expresia catch să definim o variabilă de acest tip:
catch (const std::exception& err)
Adică aici err reprezintă o referință constantă la un obiect de tip exception. Dacă nu avem nevoie să folosim această variabilă în blocul catch, putem indica doar tipul excepției:
catch (std::exception)
{
std::cout << "Error!!!" << std::endl;
}
Prin intermediul funcției what() putem obține un mesaj de eroare sub formă de șir C-style. Totuși, pentru tipul de bază std::exception această funcție nu are prea multă utilitate, deoarece doar returnează numele clasei. Însă în clasele derivate poate fi folosită pentru a afișa mesaje de eroare personalizate.
Crearea claselor derivate
Pe baza clasei std::exception putem crea propriile noastre tipuri de excepții. De exemplu:
#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();
}
private:
std::string message;
};
class Person
{
public:
Person(std::string name, unsigned age)
{
if(!age || 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.print();
}
catch (const person_error& err)
{
std::cout << "Person error: " << err.what() << std::endl;
}
catch (const std::exception&)
{
std::cout << "Something wrong" << std::endl;
}
}
int main()
{
testPerson("Tom", 38);
testPerson("Sam", 250);
}
Aici este definită clasa Person, care reprezintă un utilizator. În constructorul clasei sunt transmise numele și vârsta. Totuși, valoarea transmisă pentru vârstă poate depăși un prag rezonabil sau poate fi zero. În acest caz, generăm o excepție de tip person_error:
throw person_error("Invalid age");
Clasa person_error este derivată din std::exception, primește mesajul de eroare prin constructor și îl stochează în membrul message.
Pentru a returna mesajul, trebuie să suprascriem funcția virtuală what(). Problema este că funcția returnează un const char*, în timp ce mesajul este stocat ca std::string. Așadar, trebuie să convertim cu c_str():
return message.c_str();
Pentru test, este definită funcția testPerson, în care în blocul try se creează un obiect Person. Construcția try..catch folosește două blocuri catch: unul pentru clasa derivată person_error, și unul pentru tipul de bază exception:
catch (const person_error& err)
{
std::cout << "Person error: " << err.what() << std::endl;
}
catch (const std::exception&)
{
std::cout << "Something wrong" << std::endl;
}
În acest caz, programul va afișa:
Name: Tom Age: 38
Person error: Invalid age