Pregunta

Me encontré con Este artículo escrito por Andrei Alexandrescu y Petru Marginean hace muchos años, que presenta y analiza una clase de utilidad llamada ScopeGuard para escribir código seguro para excepciones.Me gustaría saber si codificar con estos objetos realmente conduce a un mejor código o si confunde el manejo de errores, en el sentido de que tal vez la devolución de llamada del guardia se presentaría mejor en un bloque de captura.¿Alguien tiene alguna experiencia usándolos en código de producción real?

¿Fue útil?

Solución

Definitivamente mejora tu código.Su afirmación tentativamente formulada de que es oscuro y que el código merecería una catch block simplemente no es cierto en C++ porque RAII es un modismo establecido.Manejo de recursos en C++ es se realiza mediante la adquisición de recursos y la recolección de basura se realiza mediante llamadas implícitas al destructor.

Por otro lado, explícito catch Los bloques inflarían el código e introducirían errores sutiles porque el flujo del código se vuelve mucho más complejo y el manejo de los recursos debe realizarse explícitamente.

RAII (incluyendo ScopeGuards) no es una técnica oscura en C++ sino una mejor práctica firmemente establecida.

Otros consejos

Sí.

Si hay una sola pieza de código C++ que recomendaría a cada programador de C++ que dedique 10 minutos a aprender, es ScopeGuard (ahora parte del programa disponible gratuitamente). biblioteca loki).

Decidí intentar usar una versión (ligeramente modificada) de ScopeGuard para un pequeño programa GUI Win32 en el que estaba trabajando.Win32, como sabrá, tiene muchos tipos diferentes de recursos que deben cerrarse de diferentes maneras (p. ej.Los mangos del núcleo generalmente se cierran con CloseHandle(), GDI BeginPaint() necesita ser emparejado con EndPaint(), etc.) Utilicé ScopeGuard con todos estos recursos, y también para asignar buffers de trabajo con new (p.ej.para conversiones de juegos de caracteres hacia/desde Unicode).

Lo que me sorprendió fue cuánto corta el programa era. Básicamente, es beneficioso para todos:su código se vuelve más corto y más robusto al mismo tiempo.Cambios de código futuros no se puede filtrar nada.Simplemente no pueden.¿Cuan genial es eso?

A menudo lo uso para proteger el uso de la memoria, cosas que deben liberarse y que fueron devueltas por el sistema operativo.Por ejemplo:

DATA_BLOB blobIn, blobOut;
blobIn.pbData=const_cast<BYTE*>(data);
blobIn.cbData=length;

CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobOut);
Guard guardBlob=guardFn(::LocalFree, blobOut.pbData);
// do stuff with blobOut.pbData

No he usado esta plantilla en particular, pero he usado algo similar antes.Sí, conduce a un código más claro en comparación con un código igualmente sólido implementado de diferentes maneras.

Creo que a las respuestas anteriores les falta una nota importante.Como otros han señalado, puedes usar ScopeGuard para liberar los recursos asignados independientemente del fallo (excepción).Pero puede que esa no sea la única cosa para la que quizás quieras utilizar el protector de alcance.De hecho, los ejemplos en el artículo vinculado utilizan ScopeGuard con un propósito diferente:transcaciones.En resumen, podría ser útil si tiene varios objetos (incluso si esos objetos usan RAII correctamente) que necesita mantener en un estado que de alguna manera esté correlacionado.Si el cambio de estado de cualquiera de esos objetos da como resultado una excepción (lo que, supongo, generalmente significa que su estado no cambió), entonces todos los cambios ya aplicados deben revertirse.Esto crea su propio conjunto de problemas (¿qué pasa si la reversión también falla?).Podría intentar implementar su propia clase que administre dichos objetos correlacionados, pero a medida que aumente el número de ellos, se volvería complicado y probablemente volvería a usar ScopeGuard internamente de todos modos.

Sí.

Era tan importante en C++ que incluso se creó una sintaxis especial en D:

void somefunction() {
    writeln("function enter");
    // c++ has similar constructs but not in syntax level
    scope(exit) writeln("function exit");

    // do what ever you do, you never miss the function exit output
}

Tengo que decir que no, no, no es así.Las respuestas aquí ayudan a demostrar por qué es una idea realmente terrible.El manejo de recursos debe realizarse a través de clases reutilizables.Lo único que han logrado al usar un protector de alcance es violar DRY up the wazoo y duplicar su código de liberación de recursos en todo su código base, en lugar de escribir una clase para manejar el recurso y eso es todo, para todo.

Si Los guardias de alcance tienen algún uso real, el manejo de recursos es no uno de ellos.En ese caso, son enormemente inferiores al RAII simple, ya que RAII está deduplicado y es automático y los protectores de alcance son duplicación o interrupción manual del código.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top