Ответ
Выброс исключения из конструктора — стандартный и безопасный способ сообщить об ошибке инициализации. Его ключевые особенности:
- Объект считается не созданным. Деструктор этого объекта не будет вызван, так как его жизненный цикл никогда не начинался.
- Частично созданные подобъекты разрушаются. Для уже сконструированных на момент исключения подобъектов (базовых классов и членов-объектов) деструкторы будут вызваны в обратном порядке. Это гарантирует язык.
- Исключение должно быть обработано снаружи. Конструктор не имеет возвращаемого типа, поэтому исключение — единственный способ сигнализировать о сбое.
Практический пример с управлением ресурсами:
#include <iostream>
#include <stdexcept>
#include <memory>
class FileHandle {
FILE* m_file;
public:
FileHandle(const char* filename) : m_file(nullptr) {
m_file = fopen(filename, "r");
if (!m_file) {
// 1. Исключение в конструкторе
throw std::runtime_error("Failed to open file");
}
std::cout << "File opened.n";
}
~FileHandle() {
if (m_file) {
fclose(m_file);
std::cout << "File closed.n";
}
}
};
class DatabaseConnection {
std::string m_connectionString;
public:
DatabaseConnection(const std::string& connStr) : m_connectionString(connStr) {
// Имитация сбоя подключения
throw std::runtime_error("Connection failed");
std::cout << "DB connected.n";
}
~DatabaseConnection() {
std::cout << "DB disconnected.n";
}
};
class ResourceManager {
FileHandle m_file; // Член 1
DatabaseConnection m_db; // Член 2
public:
ResourceManager()
: m_file("data.txt"), // Сконструируется успешно
m_db("localhost") // Выбросит исключение
{
std::cout << "ResourceManager fully constructed.n";
}
~ResourceManager() {
std::cout << "ResourceManager destroyed.n";
}
};
int main() {
try {
ResourceManager rm; // Конструктор выбросит исключение
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << "n";
}
return 0;
}
Вывод программы:
File opened.
File closed.
Caught: Connection failed
Что произошло:
- Сконструировался
m_file. - При конструировании
m_dbвыброшено исключение. - Для частично созданного объекта
ResourceManagerвызван деструктор~FileHandle()(членm_file), но не вызван деструктор~ResourceManager()(объект не создан). - Исключение передано в
main().