Что стоит помнить при использовании исключений в конструкторе объекта?
Ответ: Если исключение не обработано, то c логической точки зрения разрушается объект, который еще не создан, а с технической, так как он еще не создан, то и деструктор этого объекта не будет вызван.
Пример:
Output:
Hello from Base()
Exception message: Something failed
Я немного модифицировал предыдущий пример, чтобы проблема была наглядней. Здесь объект m_hFile (если был открыт) утечет т.к. до CloseHandle() выполнение не дойдет. Т.е. имеем такие же проблемы как в первом примере: возможная утечка ресурсов или другие проблемы из-за нарушения логики работы класса.
Здесь могут спросить: «Как бы вы поступили при подобной ситуации». Правильный ответ: «Воспользовался бы умными указателями». Простой пример умного указателя:
Теперь и без вызова деструктора Base хэндл будет закрыт, т.к. при уничтожении класса Base будет уничтожен объект m_hFile класса CHandle, в деструкторе которого и будет закрыт хэндл.
Изобретать велосипед, конечно, не надо, все уже написано до нас, это пример который можно написать на бумажке при соответствующем вопросе. А так есть boost, Loki, ATL и т.п., где это уже реализовано.
#cpp #programming
👉 @cpp_lib
Ответ: Если исключение не обработано, то c логической точки зрения разрушается объект, который еще не создан, а с технической, так как он еще не создан, то и деструктор этого объекта не будет вызван.
Пример:
class Base
{
private:
HANDLE m_hFile;
public:
Base()
{
std::cout << "Hello from Base()" << std::endl;
m_hFile = ::CreateFileA(...);
// Вызываем код, который в ходе своего выполнения бросает исключение
SomeLib.SomeFunc(...);
}
virtual ~Base()
{
std::cout << "Hello from ~Base()" << std::endl;
// Здесь мы планировали закрыть хэндл
::CloseHandle(m_hFile);
}
};
try
{
Base b;
}
catch(const std::exception &e)
{
std::cout << "Exception message: " << e.what() << std::endl;
}
Output:
Hello from Base()
Exception message: Something failed
Я немного модифицировал предыдущий пример, чтобы проблема была наглядней. Здесь объект m_hFile (если был открыт) утечет т.к. до CloseHandle() выполнение не дойдет. Т.е. имеем такие же проблемы как в первом примере: возможная утечка ресурсов или другие проблемы из-за нарушения логики работы класса.
Здесь могут спросить: «Как бы вы поступили при подобной ситуации». Правильный ответ: «Воспользовался бы умными указателями». Простой пример умного указателя:
class Base
{
private:
class CHandle
{
public:
~CHandle()
{
::CloseHandle(m_handle);
}
private:
HANDLE m_handle;
public:
// Для полноценного smart pointer'а перегрузки одной операции
// не достаточно, но для нашего примера и понимания вполне хватит
void operator = (const HANDLE &handle)
{
m_handle = handle;
}
};
CHandle m_hFile;
public:
Base()
{
std::cout << "Hello from Base()" << std::endl;
m_hFile = ::CreateFileA(...);
// Вызываем код, который в ходе своего выполнения бросает исключение
SomeLib.SomeFunc(...);
}
virtual ~Base()
{
std::cout << "Hello from ~Base()" << std::endl;
}
...Теперь и без вызова деструктора Base хэндл будет закрыт, т.к. при уничтожении класса Base будет уничтожен объект m_hFile класса CHandle, в деструкторе которого и будет закрыт хэндл.
Изобретать велосипед, конечно, не надо, все уже написано до нас, это пример который можно написать на бумажке при соответствующем вопросе. А так есть boost, Loki, ATL и т.п., где это уже реализовано.
#cpp #programming
👉 @cpp_lib