RAII
RAII (Resource Acquisition Is Initialization) - это идиома программирования на C++, суть которой заключается в том, что захват ресурса происходит в конструкторе объекта, а его освобождение - в деструкторе. Такой подход гарантирует, что ресурс будет освобожден автоматически при выходе объекта из области видимости, даже если во время выполнения программы произойдет исключение. Ресурсом может быть динамическая память, файловый дескриптор, сетевой сокет, мьютекс и многое другое.
Ключевая идея RAII состоит в том, что время жизни ресурса привязывается к времени жизни объекта. Пока объект существует - ресурс удерживается, как только объект уничтожается - ресурс освобождается. Это избавляет программиста от необходимости вручную вызывать освобождающие функции и защищает от утечек ресурсов.
Синтаксис
Класс, реализующий идиому RAII, обычно имеет следующую структуру:
class ResourceGuard
{
public:
ResourceGuard()
{
// захват ресурса
}
~ResourceGuard()
{
// освобождение ресурса
}
// запрет копирования (при необходимости)
ResourceGuard(const ResourceGuard&) = delete;
ResourceGuard& operator=(const ResourceGuard&) = delete;
};
Пример
Давайте напишем класс, который управляет динамически выделенной памятью по идиоме RAII. Память захватывается в конструкторе, а освобождается в деструкторе:
#include <iostream>
using namespace std;
class IntGuard
{
private:
int* ptr;
public:
IntGuard(int value)
{
ptr = new int(value);
cout << "Resource acquired" << endl;
}
~IntGuard()
{
delete ptr;
cout << "Resource released" << endl;
}
int get() const
{
return *ptr;
}
};
int main()
{
IntGuard guard(42);
cout << guard.get() << endl;
return 0;
}
Результат выполнения кода:
"Resource acquired"
42
"Resource released"
Пример
Главное преимущество RAII проявляется при возникновении исключений. Давайте посмотрим, как деструктор все равно вызывается при разворачивании стека:
#include <iostream>
#include <stdexcept>
using namespace std;
class IntGuard
{
private:
int* ptr;
public:
IntGuard(int value)
{
ptr = new int(value);
cout << "Resource acquired" << endl;
}
~IntGuard()
{
delete ptr;
cout << "Resource released" << endl;
}
int get() const
{
return *ptr;
}
};
void doWork()
{
IntGuard guard(123);
throw runtime_error("Something went wrong");
}
int main()
{
try
{
doWork();
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
Результат выполнения кода:
"Resource acquired"
"Resource released"
"Something went wrong"
Пример
Стандартная библиотека C++ активно использует
идиому RAII. Например, умные указатели
unique_ptr и shared_ptr автоматически
управляют временем жизни динамической памяти:
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<int> ptr = make_unique<int>(42);
cout << *ptr << endl;
return 0;
}
Результат выполнения кода:
42