utilizados usados tipos son programacion objective mas los lenguajes lenguaje ejemplos cuales caracteristicas c++ language-agnostic raii

usados - ¿Los programadores de otros idiomas, además de C++, usan, conocen o comprenden RAII?



tipos de lenguaje de programacion y sus caracteristicas (17)

Para las personas que están comentando en este hilo sobre RAII (la adquisición de recursos es la inicialización), aquí hay un ejemplo motivacional.

class StdioFile { FILE* file_; std::string mode_; static FILE* fcheck(FILE* stream) { if (!stream) throw std::runtime_error("Cannot open file"); return stream; } FILE* fdup() const { int dupfd(dup(fileno(file_))); if (dupfd == -1) throw std::runtime_error("Cannot dup file descriptor"); return fdopen(dupfd, mode_.c_str()); } public: StdioFile(char const* name, char const* mode) : file_(fcheck(fopen(name, mode))), mode_(mode) { } StdioFile(StdioFile const& rhs) : file_(fcheck(rhs.fdup())), mode_(rhs.mode_) { } ~StdioFile() { fclose(file_); } StdioFile& operator=(StdioFile const& rhs) { FILE* dupstr = fcheck(rhs.fdup()); if (fclose(file_) == EOF) { fclose(dupstr); // XXX ignore failed close throw std::runtime_error("Cannot close stream"); } file_ = dupstr; return *this; } int read(std::vector<char>& buffer) { int result(fread(&buffer[0], 1, buffer.size(), file_)); if (ferror(file_)) throw std::runtime_error(strerror(errno)); return result; } int write(std::vector<char> const& buffer) { int result(fwrite(&buffer[0], 1, buffer.size(), file_)); if (ferror(file_)) throw std::runtime_error(strerror(errno)); return result; } }; int main(int argc, char** argv) { StdioFile file(argv[1], "r"); std::vector<char> buffer(1024); while (int hasRead = file.read(buffer)) { // process hasRead bytes, then shift them off the buffer } }

Aquí, cuando se StdioFile una instancia de StdioFile , se StdioFile el recurso (una secuencia de archivos, en este caso); cuando se destruye, el recurso se libera. No es necesario o finally se requiere un bloqueo; si la lectura causa una excepción, se llama a fclose automáticamente, porque está en el destructor.

Se garantiza que el destructor se invocará cuando la función abandone main , ya sea de manera normal o por excepción. En este caso, la secuencia de archivos se limpia. El mundo está a salvo una vez más. :-RE

Me he dado cuenta de que RAII ha estado recibiendo mucha atención en Stackoverflow, pero en mis círculos (sobre todo C ++), RAII es tan obvio que es como preguntar qué es una clase o un destructor.

Así que estoy realmente curioso si eso se debe a que estoy rodeado diariamente por los programadores de C ++ de núcleo duro, y RAII simplemente no es tan conocido en general (incluido C ++), o si todo este cuestionamiento en Stackoverflow se debe al hecho que ahora estoy en contacto con programadores que no crecieron con C ++, y en otros idiomas la gente simplemente no usa / sabe acerca de RAII?


Creo que muchos otros lenguajes (los que no tienen delete , por ejemplo) no le dan al programador el mismo control sobre la duración de los objetos, por lo que debe haber otros medios para proporcionar la eliminación determinista de los recursos. En C #, por ejemplo, el uso de IDisposable es común.


En primer lugar, estoy muy sorprendido de que no sea más conocido. Totalmente pensé que RAII era, al menos, obvio para los programadores de C ++. Sin embargo, ahora creo que puedo entender por qué la gente realmente pregunta al respecto. Estoy rodeado, y mi yo debe ser, C ++ fanáticos ...

Así que mi secreto ... Supongo que sería, que solía leer a Meyers, Sutter [EDIT:] y Andrei todo el tiempo hace años hasta que lo asimilé.


Está relacionado con saber cuándo se llamará tu destructor, ¿verdad? Por lo tanto, no es totalmente independiente del idioma, dado que eso no es un hecho en muchos lenguajes de GC.


Lo que ocurre con RAII es que requiere la finalización determinística de algo que está garantizado para objetos basados ​​en pila en C ++. Los lenguajes como C # y Java que dependen de la recolección de basura no tienen esta garantía, por lo que tienen que estar "atornillados" de alguna manera. En C # esto se hace implementando IDisposable y muchos de los mismos patrones de uso que luego surgen básicamente, uno de los motivos de la declaración de "uso", asegura la eliminación y es muy conocida y utilizada.

Así que, básicamente, el idioma está allí, simplemente no tiene un nombre elegante.


RAII es específico de C ++. C ++ tiene la combinación necesaria de objetos asignados a la pila, tiempos de vida de objetos no administrados y manejo de excepciones.


RAII es popular en C ++ porque es uno de los pocos (¿solo?) Idiomas que pueden asignar variables locales de ámbito complejo, pero no tiene una cláusula finally . C #, Java, Python, Ruby tienen finally o un equivalente. C finally no tiene, pero tampoco puede ejecutar el código cuando una variable queda fuera del alcance.


RAII significa Adquisición de recursos es inicialización . Esto no es lingüístico en absoluto. Este mantra está aquí porque C ++ funciona de la manera que funciona. En C ++, un objeto no se construye hasta que se completa su constructor. Un destructor no se invocará si el objeto no se ha construido con éxito.

Traducido al lenguaje práctico, un constructor debe asegurarse de que cubra el caso si no puede completar su trabajo a fondo. Si, por ejemplo, se produce una excepción durante la construcción, el constructor debe manejarla con elegancia, porque el destructor no estará allí para ayudarlo. Esto generalmente se hace cubriendo las excepciones dentro del constructor o reenviando esta molestia a otros objetos. Por ejemplo:

class OhMy { public: OhMy() { p_ = new int[42]; jump(); } ~OhMy() { delete[] p_; } private: int* p_; void jump(); };

Si la llamada a jump() en el constructor arroja, estamos en problemas, porque p_ tendrá fugas. Podemos arreglar esto de esta manera:

class Few { public: Few() : v_(42) { jump(); } ~Few(); private: std::vector<int> v_; void jump(); };

Si las personas no son conscientes de esto, se debe a una de estas dos cosas:

  • Ellos no conocen bien C ++. En este caso, deberían volver a abrir TCPPPL antes de escribir su próxima clase. Específicamente, la sección 14.4.1 en la tercera edición del libro habla sobre esta técnica.
  • Ellos no conocen C ++ en absoluto. Esta bien. Esta expresión es muy C ++ y. O aprende C ++ u olvida todo sobre esto y continúa con tus vidas. Preferentemente aprender C ++. ;)

Uso C ++ ˚ RAII todo el tiempo, pero también he desarrollado en VB6 durante mucho tiempo y RAII siempre ha sido un concepto ampliamente utilizado allí (aunque nunca he oído que nadie lo llame así).

De hecho, muchos programas VB6 confían bastante en RAII. Uno de los usos más curiosos que he visto repetidamente es la siguiente clase pequeña:

'' WaitCursor.cls '' Private m_OldCursor As MousePointerConstants Public Sub Class_Inititialize() m_OldCursor = Screen.MousePointer Screen.MousePointer = vbHourGlass End Sub Public Sub Class_Terminate() Screen.MousePointer = m_OldCursor End Sub

Uso:

Public Sub MyButton_Click() Dim WC As New WaitCursor '' … Time-consuming operation. '' End Sub

Una vez que termina la operación que consume mucho tiempo, el cursor original se restaura automáticamente.


Tengo colegas que son hard-core, "leen la especificación" tipos de C ++. Muchos de ellos conocen RAII pero nunca lo he escuchado usar fuera de esa escena.


El problema con RAII es el acrónimo. No tiene una correlación obvia con el concepto. ¿Qué tiene esto que ver con la asignación de pila? Eso es a lo que se reduce. C ++ le brinda la capacidad de asignar objetos en la pila y garantizar que se invoquen sus destructores cuando se desenrolla la pila. A la luz de eso, ¿RAII suena como una forma significativa de encapsular eso? No. Nunca escuché acerca de RAII hasta que vine aquí hace unas semanas, e incluso tuve que reírme mucho cuando leí que alguien había publicado que nunca contrataría a un programador de C ++ que no supiera qué era RAII. Sin duda, el concepto es bien conocido por la mayoría de los desarrolladores competentes profesionales de C ++. Es solo que el acrónimo está mal concebido.


Hay muchas razones por las que RAII no se conoce mejor. Primero, el nombre no es particularmente obvio. Si yo no supiera ya qué RAII era, nunca lo adivinaría por el nombre. (¿La adquisición de recursos es la inicialización? ¿Qué tiene eso que ver con el destructor o la limpieza, que es lo que realmente caracteriza a RAII?)

Otra es que no funciona tan bien en idiomas sin una limpieza determinista.

En C ++, sabemos exactamente cuándo se llama al destructor, sabemos el orden en el que se invocan los destructores, y podemos definirlos para hacer lo que queramos.

En la mayoría de los idiomas modernos, todo es basura, lo que hace que RAII sea más complicado de implementar. No hay ninguna razón por la cual no sería posible agregar extensiones RAII a, digamos, C #, pero no es tan obvio como en C ++. Pero como otros lo han mencionado, Perl y otros lenguajes soportan RAII a pesar de ser basura recolectada.

Dicho esto, aún es posible crear su propio envoltorio de estilo RAII en C # u otros idiomas. Lo hice en C # hace un tiempo. Tuve que escribir algo para asegurarme de que la conexión a la base de datos se cerrara inmediatamente después del uso, una tarea que cualquier programador de C ++ vería como un candidato obvio para RAII. Por supuesto, podríamos incluir todo en el using declaraciones cada vez que usamos una conexión db, pero eso es simplemente desordenado y propenso a errores.

Mi solución fue escribir una función auxiliar que tomara un delegado como argumento, y luego cuando se llamara, se abrió una conexión a la base de datos, y dentro de una instrucción using, se pasó a la función de delegado, pseudocódigo:

T RAIIWrapper<T>(Func<DbConnection, T> f){ using (var db = new DbConnection()){ return f(db); } }

Todavía no es tan bueno ni tan obvio como C ++ - RAII, pero logró aproximadamente lo mismo. Siempre que necesitemos una conexión Db, debemos llamar a esta función auxiliar que garantiza que se cerrará después.



CPython (el Python oficial escrito en C) admite RAII debido a su uso de objetos contados de referencia con destrucción basada en el alcance inmediata (en lugar de cuando se recoge la basura). Desafortunadamente, Jython (Python en Java) y PyPy no son compatibles con este idioma RAII muy útil y rompe una gran cantidad de código de Python heredado. Por lo tanto, para Python portátil, debe manejar todas las excepciones manualmente como Java.


RAII es una forma en C ++ de asegurarse de que se ejecute un procedimiento de limpieza después de un bloque de código independientemente de lo que ocurra en el código: el código se ejecuta hasta el final correctamente o genera una excepción. Un ejemplo ya citado cierra automáticamente un archivo después de su procesamiento, ver respuesta aquí .

En otros idiomas, usa otro mecanismo para lograr eso.

En Java, tienes construcciones try {} finally {}:

try { BufferedReader file = new BufferedReader(new FileReader("infilename")); // do something with file } finally { file.close(); }

En Ruby tienes el argumento de bloque automático:

File.open("foo.txt") do | file | # do something with file end

En Lisp, has unwind-protect y la predefinida with-XXX

(with-open-file (file "foo.txt") ;; do something with file )

En Scheme tienes dynamic-wind y el predefinido with-XXXXX :

(with-input-from-file "foo.txt" (lambda () ;; do something )

en Python tienes intentar finalmente

try file = open("foo.txt") # do something with file finally: file.close()

La solución C ++ como RAII es bastante torpe porque te obliga a crear una clase para todos los tipos de limpieza que tienes que hacer. Esto puede obligarte a escribir un montón de pequeñas clases tontas.

Otros ejemplos de RAII son:

  • desbloqueo de un mutex después de la adquisición
  • cerrando una conexión a la base de datos después de abrir
  • liberar memoria después de la asignación
  • iniciar sesión en la entrada y salida de un bloque de código
  • ...

RAII.

Comienza con un constructor y un destructor, pero es más que eso.
Se trata de controlar de forma segura los recursos en presencia de excepciones.

Lo que hace que RAII sea superior a finalmente y a tales mecanismos es que hace que el código sea más seguro de usar porque traslada la responsabilidad de usar un objeto correctamente desde el usuario del objeto hasta el diseñador del objeto.

Lee esto

Ejemplo para usar StdioFile correctamente usando RAII.

void someFunc() { StdioFile file("Plop","r"); // use file } // File closed automatically even if this function exits via an exception.

Para obtener la misma funcionalidad con finalmente.

void someFunc() { // Assuming Java Like syntax; StdioFile file = new StdioFile("Plop","r"); try { // use file } finally { // close file. file.close(); // // Using the finaliser is not enough as we can not garantee when // it will be called. } }

Como tiene que agregar explícitamente el bloque try {} finally {}, esto hace que este método de codificación sea más propenso a errores ( es decir , es el usuario del objeto el que debe pensar en las excepciones). Al usar la excepción de RAII, la seguridad debe codificarse una vez cuando se implementa el objeto.

A la pregunta es este C ++ específico.
Respuesta corta: No.

Respuesta más larga:
Requiere Constructores / Destructores / Excepciones y objetos que tienen una vida útil definida.

Bien técnicamente no necesita excepciones. Simplemente se vuelve mucho más útil cuando se pueden usar excepciones, ya que hace que el control del recurso en presencia de excepciones sea muy fácil.
Pero es útil en todas las situaciones donde el control puede dejar una función antes de tiempo y no ejecutar todo el código ( por ejemplo, retorno anticipado de una función. Es por eso que múltiples puntos de retorno en C son mal olor de código, mientras que múltiples puntos de retorno en C ++ no son olor a código [porque podemos limpiar usando RAII]).

En C ++ la vida útil controlada se logra mediante variables de pila o punteros inteligentes. Pero este no es el único momento en que podemos tener una vida útil estrechamente controlada. Por ejemplo, los objetos Perl no se basan en la pila, pero tienen una vida útil muy controlada debido al recuento de referencias.


Una modificación de la respuesta de @ Pierre :

En Python:

with open("foo.txt", "w") as f: f.write("abc")

f.close() se llama automáticamente si se generó una excepción o no.

En general, se puede hacer usando contextlib.closing , de la documentación:

closing(thing) : devuelve un administrador de contexto que cierra una cosa al finalizar el bloque. Esto es básicamente equivalente a:

from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close()

Y te permite escribir un código como este:

from __future__ import with_statement # required for python version < 2.6 from contextlib import closing import urllib with closing(urllib.urlopen(''http://www.python.org'')) as page: for line in page: print line

sin necesidad de cerrar explícitamente la página. Incluso si ocurre un error, se invocará a page.close () cuando se salga del bloque with.