serie schaum práctico programación programacion practico enfoque c++ raii

c++ - schaum - ¿Qué significa adquisición de recursos es la inicialización(RAII)?



programación en c++ un enfoque práctico. serie schaum (7)

"RAII" significa "Adquisición de recursos es inicialización" y en realidad es un nombre poco apropiado, ya que no se trata de la adquisición de recursos (y la inicialización de un objeto), sino de la liberación del recurso (mediante la destrucción de un objeto). ).
Pero RAII es el nombre que tenemos y se pega.

En su corazón, el lenguaje presenta recursos de encapsulación (trozos de memoria, archivos abiertos, exclusión mutua, tu nombre) en objetos locales y automáticos , y el destructor de ese objeto libera el recurso cuando el objeto se destruye en el lugar. Fin del ámbito al que pertenece:

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

Por supuesto, los objetos no son siempre objetos locales, automáticos. Podrían ser miembros de una clase, también:

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

Si tales objetos administran la memoria, a menudo se los llama "punteros inteligentes".

Hay muchas variaciones de esto. Por ejemplo, en los primeros fragmentos de código surge la pregunta de qué pasaría si alguien quisiera copiar obj . La salida más fácil sería simplemente rechazar la copia. std::unique_ptr<> , un puntero inteligente para ser parte de la biblioteca estándar tal como aparece en el próximo estándar de C ++, hace esto.
Otro puntero inteligente de este tipo, std::shared_ptr presenta la "propiedad compartida" del recurso (un objeto asignado dinámicamente) que posee. Es decir, se puede copiar libremente y todas las copias se refieren al mismo objeto. El puntero inteligente realiza un seguimiento de cuántas copias se refieren al mismo objeto y lo eliminará cuando se destruya la última.
std::auto_ptr presenta una tercera variante que implementa un tipo de movimiento semántico: un objeto es propiedad de un solo puntero, y el intento de copiar un objeto resultará (a través de la piratería de sintaxis) en transferir la propiedad del objeto al objetivo. de la operación de copia.

¿Qué significa adquisición de recursos es la inicialización (RAII)?


El libro C ++ Programming with Design Patterns Revealed describe RAII como:

  1. Adquiriendo todos los recursos.
  2. Utilizando recursos
  3. Liberar recursos

Dónde

  • Los recursos se implementan como clases, y todos los punteros tienen envoltorios de clase a su alrededor (lo que los convierte en punteros inteligentes).

  • Los recursos se adquieren invocando a sus constructores y se liberan implícitamente (en orden inverso de adquisición) invocando a sus destructores.


Es un nombre realmente terrible para un concepto increíblemente poderoso, y quizás una de las cosas número 1 que los desarrolladores de C ++ se pierden cuando cambian a otros idiomas. Ha habido un pequeño movimiento para tratar de cambiar el nombre de este concepto como Scope-Bound Resource Management , aunque parece que todavía no se ha puesto de manifiesto.

Cuando decimos ''Recurso'', no solo nos referimos a la memoria, sino que puede tratarse de identificadores de archivos, sockets de red, identificadores de bases de datos, objetos GDI ... En resumen, las cosas de las que tenemos un suministro limitado y, por lo tanto, necesitamos poder controlar su uso. El aspecto ''enlazado al alcance'' significa que la vida útil del objeto está vinculada al alcance de una variable, por lo que cuando la variable se sale del alcance, el destructor liberará el recurso. Una propiedad muy útil de esto es que permite una mayor seguridad de excepción. Por ejemplo, compara esto:

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

Con el RAII one

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

En este último caso, cuando se lanza la excepción y la pila se desenrolla, las variables locales se destruyen, lo que garantiza que nuestro recurso se limpie y no se filtre.


Este es un lenguaje de programación que significa brevemente que

  • encapsular un recurso en una clase (cuyo constructor normalmente, pero no necesariamente **, adquiere el recurso y su destructor siempre lo libera)
  • utilizar el recurso a través de una instancia local de la clase *
  • el recurso se libera automáticamente cuando el objeto queda fuera del alcance

Esto garantiza que pase lo que pase mientras el recurso está en uso, eventualmente se liberará (ya sea debido a un retorno normal, a la destrucción del objeto contenedor o a una excepción lanzada).

Es una buena práctica ampliamente utilizada en C ++, ya que además de ser una forma segura de gestionar recursos, también hace que su código sea mucho más limpio, ya que no necesita mezclar código de manejo de errores con la funcionalidad principal.

* Actualización: "local" puede significar una variable local, o una variable miembro no estática de una clase. En este último caso, la variable miembro se inicializa y se destruye con su objeto propietario.

** Update2: como señaló @sbi, el recurso, aunque a menudo se asigna dentro del constructor, también puede asignarse fuera y pasarse como parámetro.


Hay tres partes en una clase RAII:

  1. El recurso se abandona en el destructor.
  2. Las instancias de la clase se asignan a la pila.
  3. El recurso se adquiere en el constructor. Esta parte es opcional, pero común.

RAII significa "Adquisición de recursos es la inicialización". La parte de "adquisición de recursos" de RAII es donde comienza algo que debe terminarse más tarde, como:

  1. Abriendo un archivo
  2. Asignando algo de memoria.
  3. Adquiriendo un candado

La parte "es inicialización" significa que la adquisición ocurre dentro del constructor de una clase.

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


La gestión manual de la memoria es una pesadilla que los programadores han estado inventando formas de evitar desde la invención del compilador. Los lenguajes de programación con recolectores de basura hacen la vida más fácil, pero a costa del rendimiento. En este artículo - Eliminación del recolector de basura: a la manera de RAII , el ingeniero de Toptal Peter Goodspeed-Niklaus nos da un vistazo a la historia de los recolectores de basura y explica cómo las nociones de propiedad y préstamo pueden ayudar a eliminar los recolectores de basura sin comprometer sus garantías de seguridad.


La vida útil de un objeto está determinada por su alcance. Sin embargo, a veces necesitamos, o es útil, crear un objeto que viva independientemente del alcance donde fue creado. En C ++, el operador new se utiliza para crear dicho objeto. Y para destruir el objeto, se puede utilizar el operador delete . Los objetos creados por el operador new se asignan dinámicamente, es decir, se asignan en la memoria dinámica (también llamada almacenamiento dinámico o almacenamiento gratuito ). Por lo tanto, un objeto que fue creado por new continuará existiendo hasta que se destruya explícitamente utilizando delete .

Algunos errores que pueden ocurrir al usar new y delete son:

  • Objeto (o memoria) filtrado : usar new para asignar un objeto y olvidarse de delete el objeto.
  • Eliminación prematura (o referencia colgante ): mantener otro puntero a un objeto, delete el objeto y luego usar el otro puntero.
  • Doble eliminación : tratando de delete un objeto dos veces.

En general, se prefieren las variables de ámbito. Sin embargo, RAII se puede usar como una alternativa a la new y delete para hacer que un objeto esté vivo independientemente de su alcance. Dicha técnica consiste en llevar el puntero al objeto que se asignó en el montón y colocarlo en un objeto de identificador / administrador . Este último tiene un destructor que se encargará de destruir el objeto. Esto garantizará que el objeto esté disponible para cualquier función que quiera acceder a él, y que el objeto se destruya cuando finalice la vida útil del objeto de manejar , sin la necesidad de una limpieza explícita.

Los ejemplos de la biblioteca estándar de C ++ que utilizan RAII son std::string y std::vector .

Considera este pedazo de código:

void fn(const std::string& str) { std::vector<char> vec; for (auto c : str) vec.push_back(c); // do something }

cuando creas un vector y le presionas elementos, no te importa asignar y desasignar dichos elementos. El vector utiliza new para asignar espacio para sus elementos en el montón y delete para liberar ese espacio. Usted, como usuario de vector, no le importan los detalles de la implementación y confiará en vector para no filtrarse. En este caso, el vector es el objeto manipulador de sus elementos.

Otros ejemplos de la biblioteca estándar que usan RAII son std::shared_ptr , std::unique_ptr , y std::lock_guard .

Otro nombre para esta técnica es SBRM , abreviatura de Scope-Bound Resource Management .