Namespaces
Variants

RAII

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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.

Enlaces externos

  1. RAII en las FAQ de C++ de Stroustrup
  2. Directrices Básicas de C++ E.6 "Usar RAII para prevenir fugas"