Adquisición de Recursos es Inicialización (RAII)
La Adquisición de Recursos es Inicialización o RAII por sus siglas en inglés Resource Acquisition Is Initialization, es una técnica de programación de C++[1][2] que vincula la duración de un recurso que tiene que ser adquirido antes de ser usado (asignación de memoria de montículo, hilo de ejecución, abrir un socket, abrir un archivo, tomar un mutex, espacio de disco, conexión a una base de datos–cualquier cosa que tenga un suministro limitado) a la duración de un objeto.
RAII garantiza que el recurso se encuentre disponible a cualquier función que pueda accesar el objeto (la disponibilidad de recursos es una invariante de clase, eliminando así pruebas redundantes en tiempo de ejecución). También garantiza que todos los recursos se liberen cuando la duración de su objeto controlador llegue a su fin, en orden inverso al orden de adquisición. De la misma manera, si la adquisición de recursos falla (el constructor egresa con una excepción), todos los recursos adquiridos por cada dato miembro completamente construido y por el subobjeto base se liberan en orden inverso al de inicialización. Esto toma ventaja de las características centrales del lenguaje (duración del objeto, salida de ámbito, orden de inicialización y desenredo de pila) para eliminar fugas de recursos y garantizar seguridad de excepciones. Otro nombre para esta técnica es Administración de Recursos Vinculados a un Ámbito (SBRM por sus siglas en inglés), debido al caso de uso básico donde la duración de un objeto RAII termina debido a que sale de ámbito.
RAII puede resumirse de la manera siguiente:
- encapsula cada recurso en una clase, donde
- el constructor adquiere el recurso y establece todas las invariantes de clase o lanza una excepción si no puede hacerse;
- el destructor libera el recurso y nunca lanza una excepción;
- siempre usa el recurso mediante una instancia de una clase RAII que bien
- el objeto RAII mismo tiene duración de almacenamiento automática o temporal, o
- tiene duración que está vinculada por la duración de un objeto automático o temporal.
La semántica de movimiento hace posible transferir de manera segura la propiedad entre dos objetos, a lo largo de ámbitos, dentro y fuera de hilos, manteniendo la seguridad del recurso.
Las clases con pares de funciones open()/close()
, lock()/unlock()
, o funciones miembro init()/copyFrom()/destroy()
son ejemplo típicos de clases no-RAII:
std::mutex m; void malo() { m.lock(); // adquiere el mutex f(); // si f() lanza una excepción, el mutex nunca se libera if(!everything_ok()) return; // retorno temprano, el mutex nunca se libera m.unlock(); // si malo() llega a esta instrucción, el mutex se libera } void bueno() { std::lock_guard<std::mutex> lk(m); // clase RAII: adquisición del mutex es inicialización f(); // si f() lanza una excepción, el mutex se libera if(!everything_ok()) return; // retorno temprano, el mutex se libera } // si bueno() retorna normalmente, el mutex se libera