Spec-Zone.ru › C++

RAII

Получение ресурса есть инициализация (Resource Acquisition Is Initialization), или RAII, — это приём программирования на C++[1][2], который связывает жизненный цикл ресурса, который необходимо получить до использования (выделенная память в куче, поток выполнения, открытый сокет, открытый файл, заблокированный мьютекс, дисковое пространство, подключение к базе данных — всё, что существует в ограниченном количестве), со временем жизни объекта.

RAII гарантирует, что ресурс доступен любой функции, которая может получить доступ к объекту (доступность ресурса — это инвариант класса, устраняющий необходимость в избыточных проверках во время выполнения). Кроме того, гарантируется, что все ресурсы будут освобождены по окончании времени жизни управляющего ими объекта в порядке, обратном порядку их получения. Аналогично, если получить ресурс не удаётся (конструктор завершается с исключением), все ресурсы, полученные каждым полностью сконструированным подобъектом-членом и базовым подобъектом, освобождаются в порядке, обратном порядку инициализации. Для устранения утечек ресурсов и обеспечения безопасности при исключениях используются основные возможности языка (время жизни объекта, выход из области видимости, порядок инициализации и раскрутка стека). Другое название этого приёма — управление ресурсами, привязанное к области видимости (SBRM), по основному случаю использования, когда время жизни объекта RAII завершается при выходе из области видимости.

Принцип RAII можно сформулировать следующим образом:

  • инкапсулировать каждый ресурс в класс, где
    • конструктор получает ресурс и устанавливает все инварианты класса либо выбрасывает исключение, если это невозможно,
    • деструктор освобождает ресурс и никогда не выбрасывает исключений;
  • всегда использовать ресурс через экземпляр класса RAII, который либо
    • сам имеет автоматическую длительность хранения или временное время жизни, либо
    • имеет время жизни, ограниченное временем жизни автоматического или временного объекта.

Семантика перемещения позволяет передавать ресурсы и владение ими между объектами, в контейнеры и из них, а также между потоками, обеспечивая безопасность работы с ресурсами.

(начиная с C++11)

К типичным примерам классов, не использующих RAII, относятся классы с функциями-членами open()/close(), lock()/unlock() или init()/copyFrom()/destroy():

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 для управления ресурсами, предоставляемыми пользователем:

  • std::unique_ptr и std::shared_ptr через std::make_unique и std::make_shared для управления динамически выделяемой памятью;
  • std::lock_guard, std::unique_lock, std::shared_lock для управления мьютексами.
(начиная с C++11)

Примечания

RAII неприменим к управлению ресурсами, которые не получают до использования: процессорным временем, доступностью ядер, ёмкостью кэша, ёмкостью пула энтропии, пропускной способностью сети, потреблением электроэнергии и памятью стека. Для таких ресурсов конструктор класса C++ не может гарантировать доступность ресурса в течение всего времени жизни объекта, поэтому необходимо использовать другие средства управления ресурсами.

Внешние ссылки

  1. RAII в FAQ Страуструпа по C++
  2. Основные рекомендации по C++ E.6 «Используйте RAII для предотвращения утечек»

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/raii

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API