RAII
Resource Acquisition Is Initialization o RAII, es una técnica de programación en C++ [1] [2] que vincula el ciclo de vida de un recurso que debe ser adquirido antes de su uso (memoria heap asignada, hilo de ejecución, socket abierto, archivo abierto, mutex bloqueado, espacio en disco, conexión de base de datos—cualquier cosa que exista en cantidad limitada) con la duración de vida de un objeto.
RAII garantiza que el recurso esté disponible para cualquier función que pueda acceder al objeto (la disponibilidad del recurso es un class invariant , eliminando pruebas de runtime redundantes). También garantiza que todos los recursos se liberen cuando finaliza el tiempo de vida de su objeto controlador, en orden inverso de adquisición. Asimismo, si la adquisición del recurso falla (el constructor sale con una excepción), todos los recursos adquiridos por cada miembro y subobjeto base completamente construido se liberan en orden inverso de inicialización. Esto aprovecha las características centrales del lenguaje ( object lifetime , scope exit , order of initialization y stack unwinding ) para eliminar fugas de recursos y garantizar la seguridad de excepciones. Otro nombre para esta técnica es Scope-Bound Resource Management (SBRM), según el caso de uso básico donde el tiempo de vida de un objeto RAII finaliza debido a la salida del ámbito.
RAII se puede resumir de la siguiente manera:
- encapsular cada recurso en una clase, donde
-
- el constructor adquiere el recurso y establece todas las invariantes de clase o lanza una excepción si esto no puede hacerse,
- el destructor libera el recurso y nunca lanza excepciones;
- siempre utilice el recurso mediante una instancia de una clase RAII que
-
- tiene duración de almacenamiento automático o vida temporal por sí mismo, o
- tiene una vida que está limitada por la vida de un objeto automático o temporal.
|
La semántica de movimiento permite la transferencia de recursos y propiedad entre objetos, dentro y fuera de contenedores, y a través de hilos, garantizando la seguridad de recursos. |
(since C++11) |
Las clases con
open()
/
close()
,
lock()
/
unlock()
, o
init()
/
copyFrom()
/
destroy()
son ejemplos típicos de clases no RAII:
std::mutex m; void bad() { m.lock(); // adquirir el mutex f(); // si f() lanza una excepción, el mutex nunca se libera if (!everything_ok()) return; // retorno anticipado, el mutex nunca se libera m.unlock(); // si bad() alcanza esta sentencia, el mutex se libera } void good() { std::lock_guard<std::mutex> lk(m); // clase RAII: la adquisición del mutex es inicialización f(); // si f() lanza una excepción, el mutex se libera if (!everything_ok()) return; // retorno anticipado, el mutex se libera } // si good() retorna normalmente, el mutex se libera
La biblioteca estándar
Las clases de la biblioteca de C++ que gestionan sus propios recursos siguen RAII: std::string , std::vector , std::jthread (desde C++20) , y muchas otras adquieren sus recursos en los constructores (que lanzan excepciones en errores), los liberan en sus destructores (que nunca lanzan), y no requieren limpieza explícita.
|
Además, la biblioteca estándar ofrece varios envoltorios RAII para gestionar recursos proporcionados por el usuario:
|
(since C++11) |
Notas
RAII no se aplica a la gestión de los recursos que no se adquieren antes del uso: tiempo de CPU, disponibilidad de núcleos, capacidad de caché, capacidad del pool de entropía, ancho de banda de red, consumo de energía eléctrica, memoria de pila. Para tales recursos, un constructor de clase C++ no puede garantizar la disponibilidad del recurso durante la duración de la vida útil del objeto, y deben utilizarse otros medios de gestión de recursos.