Ir al contenido

Diferencia entre revisiones de «RAII»

De Wikipedia, la enciclopedia libre
Contenido eliminado Contenido añadido
Wikante (discusión · contribs.)
Añadido el ejemplo 2 y "Usos en otros recursos"
LePeupleALœil (discusión · contribs.)
m Reemplazos con Replacer: «si no que también»
 
(No se muestran 21 ediciones intermedias de 19 usuarios)
Línea 1: Línea 1:
"'''Adquirir Recursos es Inicializar'''", a menudo referido por sus siglas en inglés '''RAII''' (de "Resource Acquisition Is Initialization"), es un popular [[patrón de diseño]] en varios [[Programación_orientada_a_objetos|lenguajes de programación orientados a objetos]] como [[C++]], y [[Ada_(lenguaje_de_programación)|Ada]]. La técnica fue inventada por [[Bjarne Stroustrup]]<ref name="Stroustrup-RAII-invention">{{cite book
'''''RAII''''' (del inglés ''resource acquisition is initialization'', que podría traducirse como «adquirir recursos es inicializar») es un popular [[patrón de diseño]] en varios [[Programación orientada a objetos|lenguajes de programación orientados a objetos]] como [[C++]] y [[Ada (lenguaje de programación)|Ada]]. La técnica fue inventada por [[Bjarne Stroustrup]]<ref name="Stroustrup-RAII-invention">{{cita libro
| title = The Design and Evolution of C++
| título = The Design and Evolution of C++
| url = https://archive.org/details/designevolutiono0000stro
| last = Stroustrup
| first = Bjarne
| apellidos = Stroustrup
| authorlink = Bjarne Stroustrup
| nombre = Bjarne
| enlaceautor = Bjarne Stroustrup
| year = 1994
| publisher = Addison-Wesley
| año = 1994
| editorial = Addison-Wesley
| isbn = 0-201-54330-3
| isbn = 0-201-54330-3
}}</ref> para reservar y liberar recursos en C++. En este lenguaje, después de que se lance una [[Manejo_de_excepciones|excepción]], el único código fuente que seguro que va a ser ejecutado son los [[Destructor (informática) | destructores]] de objectos que residen en la [[Pila_(informática)|pila]]. Por tanto los recursos necesitan ser gestionados con objetos adecuados. Los recursos son adquiridos durante la inicialización, cuando no hay posibilidad de que sean usados antes de ser disponibles, y liberados cuando se destruyan los mismos objetos, que se garantiza que sucede incluso cuando hay errores.
}}</ref> para reservar y liberar recursos en C++. En este lenguaje, después de que una [[Manejo de excepciones|excepción]] es lanzada, el único [[código fuente]] que con seguridad es ejecutado es el de los [[Destructor (informática)|destructores]] de objetos que residen en la [[Pila (informática)|pila]]. Por lo tanto los recursos necesitan ser gestionados con objetos adecuados. Los recursos son adquiridos durante la inicialización, cuando no hay posibilidad de que sean usados antes de estar disponibles, y liberados cuando se destruyen los mismos, algo que es garantizado que suceda incluso cuando se dan errores.


La técnica RAII es vital al escribir código C++ seguro frente a exepciones: para liberar recursos antes de permitir a las excepciones que se propaguen (para evitar fugas de memoria) el desarrollador puede escribir destructores apropiados una vez y ya está, ahorrándose escribir código de "limpieza" duplicado y disperso por el código fuente entre bloques de manejo de excepciones que pueden ser ejecutados o no.
La técnica ''RAII'' es vital al escribir código C++ seguro frente a excepciones: para liberar recursos antes de permitir a las excepciones que se propaguen (para evitar fugas de memoria) el desarrollador puede escribir destructores apropiados una sola vez, ahorrándose escribir código de «limpieza» duplicado y disperso por el código fuente entre bloques de manejo de excepciones que pueden ser ejecutados o no.


A diferencia de la [[Recolector_de_basura|recolección de basura]], RAII tiene las ventajas de: saber cuándo los objetos existen y saber cuándo no.
A diferencia de la [[Recolector de basura|recolección de basura]], ''RAII'' tiene las ventajas de saber cuándo los objetos existen y cuándo no.


==Ejemplo 1==
== Ejemplo 1 ==
La siguiente clase RAII permite un posterior uso fácil de algunas funciones C usadas para el manejo de archivos, además quitándole al desarrollador la preocupación de: tener que ver en qué casos ha de cerrar los archivos.
La siguiente clase RAII permite un fácil uso posterior de algunas funciones C usadas para el manejo de archivos, además quitándole al desarrollador la preocupación de: tener que ver en qué casos ha de cerrar los archivos.
<source lang=cpp>
<syntaxhighlight lang="cpp">
#include <cstdio>
#include <cstdio>
#include <stdexcept> // Para poder usar std::runtime_error
#include <stdexcept> // Para poder usar std::runtime_error

class file {
class file
{
public:
public:
file (const char* filename)
file (const char* filename) : file_(std::fopen(filename, "w+"))
{
: file_(std::fopen(filename, "w+")) {
if (!file_) {
if (!file_)
throw std::runtime_error("error al abrir un archivo");
{
throw std::runtime_error("Error al abrir un archivo.");
}
}
}
}


~file() {
~file()
if (std::fclose(file_)) {
{
if (std::fclose(file_))
{
throw std::runtime_error("error al cerrar un archivo");
// Nota: en general es una mala práctica lanzar excepciones desde un destructor
throw std::runtime_error("Error al cerrar un archivo.");
}
}
}
}


void write (const char* str) {
void write (const char* str)
if (EOF == std::fputs(str, file_)) {
{
if (EOF == std::fputs(str, file_))
throw std::runtime_error("error al escribir en un archivos");
{
throw std::runtime_error("Error al escribir en un archivo.");
}
}
}
}
Línea 46: Línea 55:
file & operator= (const file &);
file & operator= (const file &);
};
};
</syntaxhighlight>
</source>


La clase <code>file</code> puede ser usada así:
La clase <code>file</code> puede ser usada así:
<source lang=cpp>
<syntaxhighlight lang="cpp">
void example_usage() {
void example_usage()
{
file logfile("registro_de_incidencias.txt"); // Abrimos el archivo (estamos adquiriendo un recurso).
file logfile("registro_de_incidencias.txt"); // Abrimos el archivo (estamos adquiriendo un recurso).
logfile.write("¡Hola, registro!");
logfile.write("¡Hola, registro!");
Línea 57: Línea 67:
// debido a que el archivo se cierra automáticamente cuando se termina la vida de logfile.
// debido a que el archivo se cierra automáticamente cuando se termina la vida de logfile.
}
}
</syntaxhighlight>
</source>


==Ejemplo 2==
== Ejemplo 2 ==
Se puede comparar los siguientes ejemplos en C y C++:
Se puede comparar los siguientes ejemplos en C y C++:


<source lang="c">
<syntaxhighlight lang="c">
/* Versión en C */
/* Versión en C */
#include <stdlib.h>
#include <stdlib.h>
Línea 68: Línea 78:
void f(int n)
void f(int n)
{
{
int* array = calloc(n, sizeof(int));
int* array = calloc(n, sizeof(int));
realizar_otras_operaciones();
realizar_otras_operaciones();
free(array);
free(array);
}
}
</syntaxhighlight>
</source>


<source lang="cpp">
<syntaxhighlight lang="cpp">
// Versión en C++.
// Versión en C++.
#include <vector>
#include <vector>
Línea 80: Línea 90:
void f(int n)
void f(int n)
{
{
std::vector<int> array (n);
std::vector<int> array (n);
realizar_otras_operaciones();
realizar_otras_operaciones();
}
}
</syntaxhighlight>
</source>


La versión en C requiere que el desarrollador haga la liberación de memoria en cada posible caso, a diferencia de la versión en C++.
La versión en C requiere que el desarrollador libere la memoria en cada caso posible, a diferencia de la versión en C++.


==Usos en otros recursos==
== Ejemplo 3 ==
El lenguaje de programación Python cuenta con la palabra clave <code>with</code> , la cual permite hacer uso de objetos que implementen los métodos <code>__open__</code> y <code>__close__</code>. Al usar esta palabra clave, Python llamará al método <code>__open__</code> del objeto. Una vez se abandona el ámbito de la sentencia <code>with</code>, Python llamará automáticamente al método <code>__close__</code>. Esto resulta muy útil, porque no sólo se llamará este método cuando finaliza el ámbito, sino que también se llamará cuando ocurra cualquier excepción. De este modo, el control de apertura y cerrado de elementos es muy sencillo. Por ejemplo, se presentan la siguiente forma de abrir un archivo:<syntaxhighlight lang="python3">
with open("archivo.txt", "w") as archivo:
archivo.write("Hola Mundo!")
</syntaxhighlight>La cual, es funcionalmente parecida a:<syntaxhighlight lang="python3">
archivo = open("archivo.txt", "w")
archivo.write("Hola Mundo!")
archivo.close()
</syntaxhighlight>No obstante, si ocurriese (por algún motivo) una excepción en la ejecución del método <code>write</code>, el descriptor del archivo nunca sería cerrado. Por ello, para que fuera funcionalmente idéntico, sería necesario realizar la siguiente modificación:<syntaxhighlight lang="python3">
archivo = open("archivo.txt", "w")
try:
archivo.write("Hola Mundo!")
finally:
archivo.close()
</syntaxhighlight>


RAII evita la sobrecarga de los esquemas de la [[Recolector_de_basura|recolección de basura]], e incluso puede ser aplicado a otros recursos como:
== Usos en otros recursos ==
RAII evita la sobrecarga de los esquemas de la [[Recolector de basura|recolección de basura]], e incluso puede ser aplicado a otros recursos como:
* "Handles" a archivos, que la recolección de basura "mark-and-sweep" no maneja tan efectivamente
* Ventanas que han de ser cerradas
* Iconos en el área de notificación que han de ser ocultados
* Código de sincronización como monitores, secciones críticas, etc. que deben ser liberados para permitir que otros hilos de ejecución("threads") los obtengan
* "Handles" al registro de Windows que están abiertos
* Conexiones de red
* Objetos GDI de Windows
* Acciones a realizar cuando se termina una función (o bloque de código) en cualquier punto posible (la acción la realiza el destructor de un objeto creado cuando empieza la función)


* ''Handles'' a archivos, que la recolección de basura ''mark-and-sweep'' no maneja con igual eficiencia.
==References==
* Ventanas que han de ser cerradas.
{{reflist}}
* Iconos en el área de notificación que han de ser ocultados.
* Código de sincronización como monitores, secciones críticas, etc. que deben ser liberados para permitir que otros hilos de ejecución (''threads'') los obtengan.
* ''Handles'' al registro de [[Microsoft Windows]] que estén abiertos.
* Conexiones de red.
* Objetos [[Graphics Device Interface|GDI]] de Windows.
* Acciones a realizar cuando se termina una función (o [[bloque de código]]) en cualquier punto posible (la acción la realiza el destructor de un objeto creado cuando empieza la función).


== Referencias ==
{{listaref}}


{{Control de autoridades}}
[[Categoría:Programación orientada a objetos]]


[[Categoría:Programación orientada a objetos]]
[[de:Ressourcenbelegung ist Initialisierung]]
[[en:Resource Acquisition Is Initialization]]
[[fr:RAII]]
[[ja:RAII]]
[[pl:Resource Acquisition Is Initialization]]
[[pt:Aquisição de Recurso é Inicialização]]
[[ru:Получение ресурса есть инициализация]]

Revisión actual - 22:09 27 feb 2024

RAII (del inglés resource acquisition is initialization, que podría traducirse como «adquirir recursos es inicializar») es un popular patrón de diseño en varios lenguajes de programación orientados a objetos como C++ y Ada. La técnica fue inventada por Bjarne Stroustrup[1]​ para reservar y liberar recursos en C++. En este lenguaje, después de que una excepción es lanzada, el único código fuente que con seguridad es ejecutado es el de los destructores de objetos que residen en la pila. Por lo tanto los recursos necesitan ser gestionados con objetos adecuados. Los recursos son adquiridos durante la inicialización, cuando no hay posibilidad de que sean usados antes de estar disponibles, y liberados cuando se destruyen los mismos, algo que es garantizado que suceda incluso cuando se dan errores.

La técnica RAII es vital al escribir código C++ seguro frente a excepciones: para liberar recursos antes de permitir a las excepciones que se propaguen (para evitar fugas de memoria) el desarrollador puede escribir destructores apropiados una sola vez, ahorrándose escribir código de «limpieza» duplicado y disperso por el código fuente entre bloques de manejo de excepciones que pueden ser ejecutados o no.

A diferencia de la recolección de basura, RAII tiene las ventajas de saber cuándo los objetos existen y cuándo no.

Ejemplo 1

[editar]

La siguiente clase RAII permite un fácil uso posterior de algunas funciones C usadas para el manejo de archivos, además quitándole al desarrollador la preocupación de: tener que ver en qué casos ha de cerrar los archivos.

#include <cstdio>
#include <stdexcept> // Para poder usar std::runtime_error

class file 
{
public:
    file (const char* filename) : file_(std::fopen(filename, "w+")) 
    {
        if (!file_) 
        {
            throw std::runtime_error("Error al abrir un archivo.");
        }
    }

    ~file() 
    {
        if (std::fclose(file_)) 
        { 
            // Nota: en general es una mala práctica lanzar excepciones desde un destructor
            throw std::runtime_error("Error al cerrar un archivo.");
        }
    }

    void write (const char* str) 
    {
        if (EOF == std::fputs(str, file_)) 
        {
            throw std::runtime_error("Error al escribir en un archivo.");
        }
    }

private:
    std::FILE* file_;

    // Evitamos la copia y asignación, ya que no están implementadas.
    file (const file &);
    file & operator= (const file &);
};

La clase file puede ser usada así:

void example_usage() 
{
    file logfile("registro_de_incidencias.txt"); // Abrimos el archivo (estamos adquiriendo un recurso).
    logfile.write("¡Hola, registro!");
    // Continuamos usando el objeto logfile.
    // Podemos lanzar excepciones, usar la orden "return", "exit", etc. sin preocuparnos de cerrar el archivo;
    // debido a que el archivo se cierra automáticamente cuando se termina la vida de logfile.
}

Ejemplo 2

[editar]

Se puede comparar los siguientes ejemplos en C y C++:

/* Versión en C */
#include <stdlib.h>

void f(int n)
{
    int* array = calloc(n, sizeof(int));
    realizar_otras_operaciones();
    free(array);
}
// Versión en C++.
#include <vector>

void f(int n)
{
    std::vector<int> array (n);
    realizar_otras_operaciones();
}

La versión en C requiere que el desarrollador libere la memoria en cada caso posible, a diferencia de la versión en C++.

Ejemplo 3

[editar]

El lenguaje de programación Python cuenta con la palabra clave with , la cual permite hacer uso de objetos que implementen los métodos __open__ y __close__. Al usar esta palabra clave, Python llamará al método __open__ del objeto. Una vez se abandona el ámbito de la sentencia with, Python llamará automáticamente al método __close__. Esto resulta muy útil, porque no sólo se llamará este método cuando finaliza el ámbito, sino que también se llamará cuando ocurra cualquier excepción. De este modo, el control de apertura y cerrado de elementos es muy sencillo. Por ejemplo, se presentan la siguiente forma de abrir un archivo:

with open("archivo.txt", "w") as archivo:
	archivo.write("Hola Mundo!")

La cual, es funcionalmente parecida a:

archivo = open("archivo.txt", "w")
archivo.write("Hola Mundo!")
archivo.close()

No obstante, si ocurriese (por algún motivo) una excepción en la ejecución del método write, el descriptor del archivo nunca sería cerrado. Por ello, para que fuera funcionalmente idéntico, sería necesario realizar la siguiente modificación:

archivo = open("archivo.txt", "w")
try:
    archivo.write("Hola Mundo!")
finally:
    archivo.close()

Usos en otros recursos

[editar]

RAII evita la sobrecarga de los esquemas de la recolección de basura, e incluso puede ser aplicado a otros recursos como:

  • Handles a archivos, que la recolección de basura mark-and-sweep no maneja con igual eficiencia.
  • Ventanas que han de ser cerradas.
  • Iconos en el área de notificación que han de ser ocultados.
  • Código de sincronización como monitores, secciones críticas, etc. que deben ser liberados para permitir que otros hilos de ejecución (threads) los obtengan.
  • Handles al registro de Microsoft Windows que estén abiertos.
  • Conexiones de red.
  • Objetos GDI de Windows.
  • Acciones a realizar cuando se termina una función (o bloque de código) en cualquier punto posible (la acción la realiza el destructor de un objeto creado cuando empieza la función).

Referencias

[editar]