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 liberaif(!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 liberaif(!everything_ok())return;// retorno temprano, el mutex se libera}// si bueno() retorna normalmente, el mutex se libera
[editar]La biblioteca estándar
Las clases de la biblioteca de C++ que administran sus propios recursos siguen la técnica RAII: std::string, std::vector, std::thread, y muchas otras adquieren sus recursos en los constructores (que lanzan excepciones si hay errores), los liberan en sus destructores (que nunca lanzan), y no requieren limpieza explícita.
Además, la biblioteca estándar oferece varios envolventes RAII para administrar recursos proporcionados por el usuario:
- std::unique_ptr y std::shared_ptr para administrar memoria asignada dinámicamente, o, con un eliminador (deleter) proporcionado por el usuario, cualquier recurso representado por un puntero llano;
- std::lock_guard, std::unique_lock, std::shared_lock para administrar mutexes.
[editar]Notas
RAII no se aplica a la administración de recursos que no se adquieren antes de usarse: tiempo de la CPU, núcleos, y capacidad de la caché, capacidad de la reserva de entropía, ancho de banda de la red, consumo de potencia eléctrica, memoria de la pila.