Estoy familiarizado con las ventajas de RAII, pero recientemente he tropezado con un problema en el código como el siguiente:Cómo manejar el fracaso constructor para RAII
class Foo
{
public:
Foo()
{
DoSomething();
...
}
~Foo()
{
UndoSomething();
}
}
Todo bien, excepto que el código en la sección constructor ...
lanzó una excepción, con el resultado de que UndoSomething()
nunca recibió una llamada.
Hay maneras obvias de la fijación de ese tema en particular, como el abrigo ...
en un bloque try/catch que luego llama UndoSomething()
, sino una: eso es la duplicación de código, y b: bloques try/catch son un olor código que lo intento y evitar usando técnicas RAII. Y, es probable que el código empeore y sea más propenso a errores si hay varios pares de Do/Deshacer involucrados, y tenemos que limpiar a mitad de camino.
Me pregunto si hay un mejor enfoque para hacer esto: ¿tal vez un objeto separado toma un puntero de función e invoca la función cuando, a su vez, se destruye?
class Bar
{
FuncPtr f;
Bar() : f(NULL)
{
}
~Bar()
{
if (f != NULL)
f();
}
}
Sé que no se compilará pero debe mostrar el principio. Foo luego se convierte en ...
class Foo
{
Bar b;
Foo()
{
DoSomething();
b.f = UndoSomething;
...
}
}
Tenga en cuenta que foo ahora no requiere un destructor. ¿Eso suena como más problemas de los que vale la pena, o es este un patrón común con algo útil en el impulso para manejar el trabajo pesado para mí?
try/catch is _not_ code smell, y es a menudo subutilizado IMO. –
mira aquí: http://www.parashift.com/c++faq-lite/selfcleaning-members.html – MadScientist
@MooingDuck: De hecho, por sí mismos no huelen. Pero 'try {} catch (...) {throw;} 'tiene un fuerte olor. –