RAII
Приобретение ресурса — это инициализация или RAII — это приём программирования на C++,[1][2] который связывает жизненный цикл ресурса, который должен быть получен перед использованием (выделенная память, поток выполнения, открытый сокет, открытый файл, заблокированный мьютекс, дисковое пространство, подключение к базе данных — всё, что существует в ограниченном количестве), с жизненным циклом объекта.жизненный цикл.
RAII гарантирует, что ресурс доступен любой функции, которая может получить доступ к объекту (доступ к ресурсу — это инвариант класса, исключая избыточные проверки во время выполнения). Это также гарантирует, что все ресурсы освобождаются, когда заканчивается жизненный цикл контролирующего их объекта, в обратном порядке получения. Аналогично, если получение ресурса завершается неудачей (конструктор завершается исключением), все ресурсы, полученные каждым полностью созданным членом и базовым под-объектом, освобождаются в обратном порядке инициализации. Это использует основные возможности языка (жизненный цикл объекта, выход из области видимости, порядок инициализации и развёртывание стека), чтобы устранить утечки ресурсов и гарантировать безопасность при возникновении исключений. Другое название для этой техники — Управление ресурсами, связанными с областью видимости (SBRM), по основному случаю использования, когда жизненный цикл объекта RAII заканчивается из-за выхода из области видимости.
RAII можно резюмировать следующим образом:
- инкапсулировать каждый ресурс в класс, где
- конструктор получает ресурс и устанавливает все инварианты класса или генерирует исключение, если это невозможно,
- деструктор освобождает ресурс и никогда не генерирует исключений;
- всегда использовать ресурс через экземпляр класса RAII, который либо
- имеет автоматическое хранение или временный жизненный цикл, или
- имеет жизненный цикл, ограниченный жизненным циклом автоматического или временного объекта.
| Семантика перемещения позволяет безопасно передавать владение ресурсами между объектами, через области видимости и в/из потоков, сохраняя безопасность ресурсов. | (с C++11) |
Классы с open()/close(), lock()/unlock(), или init()/copyFrom()/destroy() методами-членами являются типичными примерами классов, не использующих RAII:
std::mutex m;
void bad()
{
m.lock(); // acquire the mutex
f(); // if f() throws an exception, the mutex is never released
if (!everything_ok())
return; // early return, the mutex is never released
m.unlock(); // if bad() reaches this statement, the mutex is released
}
void good()
{
std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization
f(); // if f() throws an exception, the mutex is released
if (!everything_ok())
return; // early return, the mutex is released
} // if good() returns normally, the mutex is releasedСтандартная библиотека
Классы библиотек C++, которые управляют своими ресурсами, следуют RAII: std::string, std::vector, std::jthread(с C++20), и многие другие получают свои ресурсы в конструкторах (которые генерируют исключения при ошибках), освобождают их в деструкторах (которые никогда не генерируют исключений) и не требуют явного освобождения.
| Кроме того, стандартная библиотека предлагает несколько обёртков RAII для управления пользовательскими ресурсами:
| (с C++11) |
Примечания
RAII не применяется к управлению ресурсами, которые не приобретаются перед использованием: время процессора, ядра и кэш-память, ёмкость пула энтропии, пропускная способность сети, потребление электроэнергии, память стека.
Внешние ссылки
- RAII в FAQ по C++ от Страуструпа
- C++ Core Guidelines E.6 «Использование RAII для предотвращения утечек»
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/raii