Для чего нужно исключение?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Назначение исключений (Exception Handling)
Исключения — это механизм обработки ошибок и исключительных ситуаций в программе. Они позволяют передать контроль из точки ошибки на несколько уровней вверх по стеку вызовов без использования кодов ошибок.
Основная проблема: возвращаемые коды
Без исключений программисты использовали возвращаемые коды ошибок:
// Старый подход с кодами ошибок
int file_read(FILE* f, char* buffer, size_t size) {
if (!f) return -1; // Ошибка: файл не открыт
if (size > MAX_SIZE) return -2; // Ошибка: размер слишком большой
if (fread(buffer, 1, size, f) == 0) return -3; // Ошибка чтения
return 0; // Успех
}
int result = file_read(f, buf, 1024);
if (result != 0) {
// Что означает -1? -2? -3? Нужно помнить коды
// Легко забыть проверить ошибку
}
Проблемы этого подхода:
- Легко забыть проверить код ошибки
- Нельзя отличить ошибку от нормального результата (если функция должна возвращать число)
- Код становится перегруженным if-проверками
- Трудно пробросить ошибку через несколько уровней функций
Решение: Исключения
// Новый подход с исключениями
void file_read(const string& filename, vector<char>& buffer) {
ifstream file(filename);
if (!file.is_open()) {
throw FileOpenException("Cannot open file: " + filename);
}
file.read(buffer.data(), buffer.size());
if (!file) {
throw ReadException("Error reading file");
}
}
// Вызывающий код
try {
file_read("data.txt", buf);
// Нормальное выполнение
} catch (const FileOpenException& e) {
cerr << "File error: " << e.what() << endl;
} catch (const ReadException& e) {
cerr << "Read error: " << e.what() << endl;
}
Преимущества исключений
1. Отделение обработки ошибок от нормального потока
Код логики становится чище:
// С исключениями
void process_user_data(const string& filename) {
auto data = load_data(filename); // Выбросит исключение если ошибка
auto cleaned = clean_data(data); // Логичный нормальный поток
save_result(cleaned);
}
// Обработка ошибок отдельно
try {
process_user_data("input.txt");
} catch (const exception& e) {
log_error(e.what());
}
2. Иерархия типов исключений
class Exception : public std::exception { };
class FileException : public Exception { };
class IOError : public FileException { };
class CorruptedData : public FileException { };
// Можно перехватить целую категорию
try {
// ...
} catch (const FileException& e) { // Перехватит ВСЕ производные
handle_file_error(e);
} catch (const Exception& e) { // Fallback для остального
handle_generic_error(e);
}
3. Гарантия распространения ошибок
Если функция не обработает исключение, оно автоматически пройдёт вверх:
void level3() {
throw runtime_error("Something wrong");
}
void level2() {
level3(); // Исключение пройдёт дальше
}
void level1() {
try {
level2();
} catch (const exception& e) {
// Перехватим здесь, хотя выброшено в level3
cerr << e.what() << endl;
}
}
RAII и исключения
Исключения идеально работают с RAII (Resource Acquisition Is Initialization):
class Database {
Connection* conn;
public:
Database(const string& url) {
conn = connect(url);
if (!conn) throw ConnectionError("Failed to connect");
}
~Database() {
if (conn) disconnect(conn); // Сработает даже при исключении!
}
};
void process() {
Database db("localhost");
// Если выброшено исключение, деструктор вызовется автоматически
query_data(db);
}
Когда НЕ использовать исключения
1. Встраиваемые системы (embedded) — часто отключают исключения 2. Очень критичные по задержкам операции — исключения имеют overhead 3. Функции low-level — могут возвращать error code
// Вместо исключений иногда используют optional/result
std::optional<int> parse_int(const string& s) {
try {
return stoi(s);
} catch (...) {
return std::nullopt;
}
}
Best Practices
1. Выбрасывай специфичные исключения:
throw FileOpenException(filename); // ✓
throw Exception(); // ✗ Слишком общее
2. Лови по const reference:
catch (const MyException& e) { } // ✓
catch (MyException e) { } // ✗ Копирует
3. Не выбрасывай исключения в деструкторах:
~MyClass() noexcept {
// Может привести к terminate() если уже обрабатывается исключение
}
Вывод: Исключения — это стандартный и безопасный способ обработки ошибок в C++, который делает код чище и надёжнее, чем коды ошибок.