¿Qué es RAII? Ejemplos?


19

Siempre cuando se usa el término RAII, las personas en realidad hablan de deconstrucción en lugar de inicialización. Creo que tengo una comprensión básica de lo que podría significar, pero no estoy muy seguro. Además: ¿es C ++ el único lenguaje RAII? ¿Qué pasa con Java o C # /. NET?

Respuestas:


25

La adquisición de recursos es la inicialización significa que los objetos deben cuidarse a sí mismos como un paquete completo y no esperar que otro código le diga a una instancia "oye, por cierto, te limpiarán pronto, ordena ahora". Por lo general, significa que hay algo significativo en el destructor. También significa que escribe una clase específicamente para administrar recursos, sabiendo que bajo ciertas circunstancias difíciles de predecir, como las excepciones, puede contar con la ejecución de destructores.

Supongamos que desea escribir un código en el que va a cambiar el cursor de Windows a un cursor de espera (reloj de arena, donut de no funcionar, etc.), haga sus cosas y luego vuelva a cambiarlo. Y diga también que "haga sus cosas" podría generar una excepción. La forma RAII de hacerlo sería hacer una clase cuyo ctor haya puesto el cursor en espera, cuyo único método "real" hiciera lo que quisieras que hiciera, y cuyo dtor hizo retroceder el cursor. Los recursos (en este caso, el estado del cursor) están vinculados al alcance de un objeto. Cuando adquiere el recurso, inicializa un objeto. Puede contar con la destrucción del objeto si se lanzan excepciones, y eso significa que puede contar con la limpieza del recurso.

Usar bien RAII significa que no necesitas finally. Por supuesto, se basa en la destrucción determinista, que no puede tener en Java. Puede obtener una especie de destrucción determinista en C # y VB.NET con using.


44
Creo que esto es a lo que se está dedicando, pero es posible que desee agregar que la razón por la cual Java y C # no admiten RAII es por el recolector de basura. En C ++, un objeto local se destruirá tan pronto como salga del alcance. En Java / C # esto no es cierto.
Jason Baker,

Ampliando el punto de Jasons, la razón por la cual Java y C # no pueden garantizar la destrucción oportuna es debido a la posibilidad de ciclos de referencia, lo que significa que es imposible determinar un orden seguro para ejecutar los destructores. Los ciclos de referencia también pueden ocurrir en C ++, pero las implicaciones son diferentes: el programador se hace responsable de determinar el orden de destrucción y hacer eliminaciones explícitas. Esa responsabilidad a menudo se empaqueta en algunos destructores de clases de nivel superior, por ejemplo, una clase contenedor es responsable de garantizar que todos los elementos contenidos se destruyan. La "propiedad" es la clave.
Steve314

1
@Jason, eso es lo que quise decir con "destrucción determinista": un programador de C ++ sabe cuándo se ejecutará el destructor.
Kate Gregory

Sé que esta es una vieja respuesta, pero todavía estoy algo confundido. Acabo de enterarme del término y alguna información dice que la adquisición debería ocurrir en el constructor. Eso realmente no tiene sentido para mí y esta respuesta parece contradecirlo, pero ¿podría aclararlo?
Por Johansson

1
@PerJohansson Sí, lo adquieres en el ctor. Y liberas en el dtor. Me estaba centrando en el segundo punto, pero van juntos. Una vez que el ctor está listo, sabes que tienes un objeto válido. Y sabe que pase lo que pase, el recurso se lanzará en el momento adecuado.
Kate Gregory

4

RAII se trata en parte de decidir cuándo un objeto se hace responsable de su propia limpieza; la regla es que el objeto se vuelve responsable si y cuando se completa la inicialización del constructor. La simetría de inicialización y limpieza, constructor y destructor, significa que los dos tienen vínculos estrechos entre sí.

Un punto de RAII es garantizar la seguridad de las excepciones: que la aplicación siga siendo coherente cuando se lanzan excepciones. A primera vista, esto es trivial: cuando una excepción hace que salga un ámbito, las variables locales en ese ámbito deben destruirse.

Pero, ¿qué sucede si el lanzamiento de excepción ocurre dentro de un constructor?

Bueno, el objeto no se ha construido completamente, por lo que no se puede destruir con seguridad. El constructor debe tener bloques de prueba según sea necesario para garantizar que se realicen las limpiezas necesarias antes de que se propague la excepción. Una vez que la excepción se propague fuera del alcance donde se construyó el objeto, no habrá una llamada de destructor, porque el objeto no se construyó en primer lugar.

Considere en particular los constructores de datos de miembros dentro del objeto que se está destruyendo. Si uno de esos arroja una excepción, su código de constructor principal no se ejecutará en absoluto, pero sí lo tendrá algún código que forme parte implícita de ese constructor. Cualquier miembro que se haya construido correctamente se destruirá automáticamente. Los miembros que no fueron construidos (incluido el que arrojó la excepción) no lo son.

Básicamente, RAII es una política que garantiza que todo lo que se construya completamente se destruirá de manera oportuna, particularmente en presencia de lanzamientos de excepción, y que cualquier objeto se construya completamente o no (no hay mitad) objetos construidos que no puedes saber cómo limpiar de forma segura). Los recursos que se asignan también se liberan. Y gran parte del trabajo está automatizado, por lo que el programador no tiene que preocuparse demasiado por ello.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.