Что подразумевается под инициализацией ресурсов (RAII)?

Что подразумевается под инициализацией ресурсов (RAII)?

Ответ 1

Это действительно ужасное имя для невероятно мощной концепции и, возможно, одной из тех вещей, которые разработчики С++ пропустили, когда они переключаются на другие языки. Было немного движения, чтобы попытаться переименовать эту концепцию как Ограниченное областью управления ресурсами, хотя, похоже, пока это не удалось поймать.

Когда мы говорим "Ресурс", мы имеем дело не только с памятью - это могут быть файловые дескрипторы, сетевые сокеты, дескрипторы баз данных, объекты GDI... Короче говоря, вещи, у которых у нас есть конечный запас, и поэтому нам нужно быть в состоянии контролировать их использование. Аспект "Связанный с областью" означает, что время жизни объекта связано с областью действия переменной, поэтому, когда переменная выходит из области видимости, деструктор освободит ресурс. Очень полезным свойством этого является то, что он обеспечивает большую безопасность исключений. Например, сравните это:

RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation();  // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks

С RAII одним

class ManagedResourceHandle {
public:
   ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
   ~ManagedResourceHandle() {delete rawHandle; }
   ... // omitted operator*, etc
private:
   RawResourceHandle* rawHandle;
};

ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();

В этом последнем случае, когда генерируется исключение, и стек разматывается, локальные переменные уничтожаются, что гарантирует, что наш ресурс очищается и не течет.

Ответ 2

Это идиома программирования, которая на короткое время означает, что вы

  • инкапсулировать ресурс в класс (конструктор которого обычно - но не обязательно ** - приобретает ресурс, и его деструктор всегда его выпускает)
  • используйте ресурс через локальный экземпляр класса *
  • ресурс автоматически освобождается, когда объект выходит из области действия

Это гарантирует, что все, что происходит во время использования ресурса, в конечном итоге будет освобождено (из-за нормального возврата, уничтожения содержащегося объекта или вызванного исключения).

Это широко используемая хорошая практика на С++, потому что, помимо безопасного использования ресурсов, она также делает ваш код намного чище, так как вам не нужно смешивать код обработки ошибок с основными функциями.

* Обновление: "local" может означать локальную переменную или нестатистическую членную переменную класса. В последнем случае переменная-член инициализируется и уничтожается с помощью объекта-владельца.

** Update2:, как указывал @sbi, ресурс, хотя часто выделяется внутри конструктора, также может быть выделен вне и передан как параметр.

Ответ 3

"RAII" означает "Инициализация ресурсов" и на самом деле является довольно неправильным, поскольку он не является приобретением ресурсов (и инициализацией объекта), с которыми он связан, но освобождает ресурс (посредством уничтожения объекта).
Но RAII - это имя, которое мы получили, и оно прилипает.

В самой своей сути идиома включает в себя инкапсуляцию ресурсов (куски памяти, открытые файлы, разблокированные мьютексы, вы-name-it) в локальных, автоматических объектах и имеющие деструктор этого объекта освобождая ресурс, когда объект уничтожается в конце области, к которой он принадлежит:

{
  raii obj(acquire_resource());
  // ...
} // obj dtor will call release_resource()

Конечно, объекты не всегда являются локальными, автоматическими объектами. Они также могут быть членами класса:

class something {
private:
  raii obj_;  // will live and die with instances of the class
  // ... 
};

Если такие объекты управляют памятью, их часто называют "умными указателями".

Есть много вариантов этого. Например, в первых фрагментах кода возникает вопрос, что произойдет, если кто-то захочет скопировать obj. Самый простой выход - просто запретить копирование. std::unique_ptr<>, умный указатель, который будет частью стандартной библиотеки, как показано на следующем стандарте С++, делает это.
Другой такой умный указатель std::shared_ptr имеет свойство "совместного использования" ресурса (динамически выделенного объекта), которое он имеет. То есть, он может свободно копироваться и все копии относятся к одному и тому же объекту. Умный указатель отслеживает, сколько копий относится к одному и тому же объекту, и удалит его, когда последний будет уничтожен.
Третий вариант представлен std::auto_ptr, который реализует своего рода семантику move: объект принадлежит только одному указателю, и попытка копирования объекта приведет (через хакерство синтаксиса) к передаче права собственности на объект на цель операция копирования.

Ответ 4

В книге C++ Programming with Design Patterns раскрыто описание RAII как:

  1. Приобретение всех ресурсов
  2. Использование ресурсов
  3. Освобождение ресурсов

где

  • Ресурсы реализуются как классы, и у всех указателей вокруг них есть обертки классов (что делает их умными указателями).

  • Ресурсы приобретаются путем вызова их конструкторов и выпущены неявно (в обратном порядке получения) путем вызова их деструкторов.

Ответ 5

Ручное управление памятью - это кошмар, который программисты изобретают способы избежать с момента изобретения компилятора. Языки программирования с сборщиками мусора облегчают жизнь, но ценой производительности. В этой статье - Устранение сборщика мусора: путь RAII, инженер Toptal Питер Гудпед-Никлаус дает нам заглянуть в историю сборщиков мусора и объясняет как понятия собственности и заимствования могут помочь устранить сборщики мусора без ущерба для их гарантий безопасности.

Ответ 6

В RAII-класс входят три части:

  1. Ресурс отказался от деструктора
  2. Экземпляры класса выделены в стеке
  3. Ресурс приобретается в конструкторе. Эта часть является необязательной, но общей.

RAII означает, что "Resource Assquisition является инициализацией". Часть "сбора ресурсов" RAII - это то, где вы начинаете то, что должно быть завершено позже, например:

  1. Открытие файла
  2. Выделение некоторой памяти
  3. Приобретение блокировки

Часть "инициализация" означает, что это происходит внутри конструктора класса.

https://www.tomdalling.com/blog/software-design/resource-acquisition-is-initialisation-raii-explained/