Да, но с важными оговорками:
unique_ptr
, shared_ptr
) помогают автоматизировать этоПример безопасного конструктора:
class ResourceHolder {
std::unique_ptr<Resource> res1;
std::unique_ptr<Resource> res2;
public:
ResourceHolder() {
res1 = std::make_unique<Resource>(); // Может бросить исключение
res2 = std::make_unique<Resource>(); // Если бросит, res1 будет корректно удалён
}
};
class Example {
std::vector<int> data;
public:
Example(size_t size) : data(size) { // Если vector бросит, объект не создастся
// Дополнительная инициализация
}
};
Для перехвата исключений из списка инициализации:
class DatabaseConnection {
Connection conn;
public:
DatabaseConnection(const std::string& params)
try : conn(params) { // Блок try для списка инициализации
// Тело конструктора
}
catch (const std::exception& e) {
// Логирование ошибки
throw; // Повторно бросаем исключение
}
};
Нет, если деструктор вызывается в процессе раскрутки стека (stack unwinding) при другом исключении. Это приведёт к вызову std::terminate
.
Логирование ошибки:
```MyClass() noexcept try {
// Код, который может генерировать ошибки
}
catch (...) {
logError("Destructor failed");
// Не бросаем исключение дальше
}
Отдельный метод для очистки:
class FileHandler {
FILE* file;
public:
void close() { // Явное закрытие с возможностью бросить исключение
if (fclose(file) != 0) {
throw FileError("Close failed");
}
}
```FileHandler() noexcept {
try { close(); }
catch (...) { /* логирование */ }
}
};
Использование std::uncaught_exceptions()
(C++17):
```MyClass() noexcept(false) { // Только если уверены в безопасности
if (std::uncaught_exceptions() == 0) {
// Можем бросать исключение, если нет активного исключения
}
}
Конструкторы:
Деструкторы:
noexcept
(неявно или явно)class Problematic {
public:
```Problematic() {
throw std::runtime_error("Error"); // Вызовет terminate(), если есть активное исключение
}
};
void test() {
try {
Problematic p;
throw std::runtime_error("First error");
} catch (...) { /* Не будет вызван */ }
}
class Leaky {
int* resource;
public:
Leaky() : resource(new int(42)) {
throw std::runtime_error("Oops"); // Утечка памяти!
}
```Leaky() { delete resource; }
};
Для конструкторов:
Для деструкторов:
close()
Резюмируем: исключения в конструкторах допустимы и часто необходимы для сигнализации об ошибках инициализации, но требуют аккуратного управления ресурсами. В деструкторах исключения крайне опасны и должны быть либо подавлены, либо преобразованы в другие формы обработки ошибок. Правильное использование RAII делает код безопасным и предсказуемым.