Содержание
Пользовательские исключения
Пользовательские исключения позволяют создавать специализированные типы ошибок для конкретных ситуаций в вашем приложении. Они наследуются от стандартных классов исключений и могут содержать дополнительную информацию об ошибке.
Пример пользовательского исключения:
#include <iostream>
#include <stdexcept>
#include <string>
// Простейшее пользовательское исключение
class MyException : public std::exception {
private:
std::string message;
public:
// Конструктор с возможностью задания сообщения
MyException(const std::string& msg) : message(msg) {}
// Переопределение метода what()
virtual const char* what() const noexcept override {
return message.c_str();
}
};
void checkValue(int value) {
if (value < 0) {
throw MyException("Отрицательное значение недопустимо");
}
if (value > 100) {
throw MyException("Значение превышает максимальный лимит");
}
std::cout << "Значение корректно: " << value << std::endl;
}
int main() {
try {
checkValue(-5);
}
catch (const MyException& e) {
std::cerr << "Поймано моё исключение: " << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << "Стандартное исключение: " << e.what() << std::endl;
}
return 0;
}
Пояснение:
Класс MyException
наследуется от std::exception
Переопределен метод what()
для возврата пользовательского сообщения
Конструктор принимает строку с описанием ошибки
Исключение бросается с помощью throw
и ловится в блоке catch
Иерархия пользовательских исключений:
Можно создавать целые иерархии исключений для разных типов ошибок:
#include <iostream>
#include <stdexcept>
#include <string>
// Базовый класс для исключений файловых операций
class FileException : public std::runtime_error {
public:
FileException(const std::string& msg, const std::string& filename)
: std::runtime_error(msg), filename(filename) {}
const std::string getFilename() const { return filename; }
private:
std::string filename;
};
// Исключение для ошибок открытия файла
class FileOpenException : public FileException {
public:
FileOpenException(const std::string& filename)
: FileException("Не удалось открыть файл", filename) {}
};
// Исключение для ошибок чтения файла
class FileReadException : public FileException {
public:
FileReadException(const std::string& filename, int line)
: FileException("Ошибка чтения файла", filename), line(line) {}
const int getLine() const { return line; }
private:
int line;
};
void processFile(const std::string& filename) {
// Имитация ошибки
if (filename.empty()) {
throw FileOpenException(filename);
}
if (filename == "corrupted.dat") {
throw FileReadException(filename, 42);
}
std::cout << "Файл " << filename << " успешно обработан" << std::endl;
}
int main() {
try {
processFile("corrupted.dat");
}
catch (const FileReadException& e) {
std::cerr << "Ошибка чтения: " << e.what()
<< "\nФайл: " << e.getFilename()
<< "\nСтрока: " << e.getLine() << std::endl;
}
catch (const FileOpenException& e) {
std::cerr << "Ошибка открытия: " << e.what()
<< "\nФайл: " << e.getFilename() << std::endl;
}
catch (const FileException& e) {
std::cerr << "Файловая ошибка: " << e.what()
<< "\nФайл: " << e.getFilename() << std::endl;
}
return 0;
}
Пояснение:
Создана иерархия: FileException
→ FileOpenException
и FileReadException
Каждый класс добавляет свою специфическую информацию (имя файла, номер строки)
Обработчики расположены от наиболее специфичных к наиболее общим
Пользовательские исключения с дополнительными данными:
Можно добавлять любые дополнительные данные в исключения:
#include <iostream>
#include <stdexcept>
#include <string>
#include <ctime>
// Исключение с временной меткой
class TimestampedException : public std::runtime_error {
public:
TimestampedException(const std::string& msg)
: std::runtime_error(msg), timestamp(std::time(nullptr)) {}
std::string getTimestamp() const {
char buf[80];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(×tamp));
return buf;
}
private:
std::time_t timestamp;
};
// Исключение для ошибок в финансовых операциях
class FinancialTransactionException : public TimestampedException {
public:
FinancialTransactionException(const std::string& msg, double amount, const std::string& account)
: TimestampedException(msg), amount(amount), account(account) {}
const double getAmount() const { return amount; }
const std::string& getAccount() const { return account; }
private:
double amount;
std::string account;
};
void processTransaction(double amount, const std::string& account) {
if (amount <= 0) {
throw FinancialTransactionException("Некорректная сумма транзакции", amount, account);
}
if (account.empty()) {
throw FinancialTransactionException("Не указан счет", amount, account);
}
std::cout << "Транзакция на сумму " << amount
<< " по счету " << account << " выполнена" << std::endl;
}
int main() {
try {
processTransaction(-100.0, "ACC123456");
}
catch (const FinancialTransactionException& e) {
std::cerr << "Ошибка транзакции [" << e.getTimestamp() << "]:\n"
<< e.what() << "\n"
<< "Сумма: " << e.getAmount() << "\n"
<< "Счет: " << e.getAccount() << std::endl;
}
return 0;
}
Пояснение:
TimestampedException
добавляет временную метку момента создания исключения
FinancialTransactionException
добавляет сумму и номер счета
Данные доступны через методы getter'ы при обработке исключения
Исключения с кодом ошибки:
Часто полезно включать код ошибки в исключение:
#include <iostream>
#include <stdexcept>
#include <string>
#include <map>
class ErrorCodeException : public std::runtime_error {
public:
ErrorCodeException(int code, const std::string& msg)
: std::runtime_error(msg), errorCode(code) {}
int getErrorCode() const { return errorCode; }
static std::string getErrorDescription(int code) {
static const std::map<int, std::string> descriptions = {
{100, "Недостаточно прав"},
{101, "Ресурс не найден"},
{102, "Неверный формат данных"},
{200, "Ошибка соединения"},
{201, "Таймаут соединения"}
};
auto it = descriptions.find(code);
return it != descriptions.end() ? it->second : "Неизвестная ошибка";
}
private:
int errorCode;
};
void checkAccess(bool isAdmin) {
if (!isAdmin) {
throw ErrorCodeException(100, "Требуются права администратора");
}
std::cout << "Доступ разрешен" << std::endl;
}
int main() {
try {
checkAccess(false);
}
catch (const ErrorCodeException& e) {
std::cerr << "Ошибка " << e.getErrorCode() << ": "
<< e.what() << "\n"
<< "Описание: "
<< ErrorCodeException::getErrorDescription(e.getErrorCode())
<< std::endl;
}
return 0;
}
Пояснение:
Исключение содержит числовой код ошибки
Статический метод getErrorDescription
предоставляет описание по коду
Коды ошибок можно использовать для локализации или логирования
Пользовательские исключения позволяют:
Создавать специализированные типы ошибок для вашего приложения
Передавать дополнительную информацию об ошибке
Строить сложные иерархии обработки ошибок
Делать код более читаемым и поддерживаемым