2010-10-14 7 views
6

En mi destructor, tengo que limpiar algunos recursos. Digamos que tengo tres llamadas para borrar los recursos que podrían arrojar. Como no es bueno dejar una excepción para dejar un destructor, ¿cuál debería ser mi patrón de diseño? Aparentemente, el camino a continuación no es escalable.Excepción en destructor

Gracias.

class B::~B(){ 

try{ 
    clearResourceA() 
} 
catch{ 
    try{ 
     clearResourceB(); 
     } 
    catch{ 
     clearResourceC(); 
    } 
    clearResourceC(); 
} 
clearResourceB(); 
    . 
    . 
} 
+0

* definitivamente * no es escalable. Al tratar de administrar solo tres recursos, ya tiene errores de lógica. – nobar

Respuesta

2

Captura cualquier cosa que pueda lanzar en su destructor con un cajón de sastre (es decir, la captura (...)) y hacer lo mejor para manejar las excepciones producidas. Asegúrese de que no se propaguen excepciones del destructor, lo que le ayudará a evitar.

10
Por qué no

:

try{clearResourceA();} catch(...){} 
try{clearResourceB();} catch(...){} 
try{clearResourceC();} catch(...){} 
0
try 
{ 
    ClearResourceA(); 
} 
catch(...) 
{ 
    ExceptionWasThrown(); 
} 
try 
{ 
    ClearResourceB(); 
} 
catch(...) 
{ 
    ExceptionWasThrown(); 
} 
... 
+0

Offtop: ¿Cómo puedo forzar la sangría? –

+0

Usa el botón "codificar", no las últimas. El botón de código tiene un 101010 en él. Escriba su código (sangría), resalte el código, luego presione el botón de código. – Starkey

+0

@Starkey: Desafortunadamente, hoy en día no existen ni esos botones ni la vista previa. Le pregunté por meta, pero la respuesta pendiente –

5

encapsular cada recurso en una clase que les borra en su destructor (con un try/catch circundante):

struct ProperlyManagedA { 
    // some means of using the resource - a rudimentary way is this: 
    A &getA() { return a; } 
    const A &getA() const { return a; } 
    // cleanup 
    ~ProperlyManagedA() { 
     try { 
      a.clear(); // whatever it is ClearResourceA actually does 
     } catch (...) {} 
    } 
    private: 
    A a; 
} 

Un shared_ptr con una costumbre delegar es una forma de lograr esto sin tener que crear una clase completa para cada tipo de recurso.

Puede mejorar descartando la excepción (log el problema, por ejemplo), dependiendo de lo que se arroje.

Mejor aún, modifique los recursos A, B y C para que se borren en sus propios destructores. Sin embargo, eso podría no ser posible.

De cualquier forma, puede colocar tantos recursos en una sola clase como desee, sin agregar ningún código al destructor de la clase. Esto es "escalabilidad". El objetivo de RAII es que cada usuario de un recurso no tenga que escribir un código de limpieza para usar el recurso correctamente.

+1

Bien sort-of. El objetivo de RAII es que la limpieza se realice en sus destructores, por lo que no debe llamar a delete o freeHandle ni a las funciones dentro de los cuerpos de su código. El problema principal aquí es que, en general, el código de limpieza no debe arrojarse. – CashCow

+0

@CashCow: "la limpieza está completa en sus destructores"; bueno, si los recursos están diseñados con los principios de RAII, entonces la limpieza se realiza en el destructor del recurso. Si tengo que escribir la clase RAII yo mismo, reconozco que ese es mi destructor. Pero quien escribe el recurso RAII, los usuarios de ese recurso RAII no tienen que escribir o llamar explícitamente al código de limpieza. Eso es lo que quise decir con "usuarios". –

+0

gracias, me estaba volviendo loco mientras miraba las otras sugerencias:/ –

2

También podría ajustar sus funciones de ClearResources para garantizar que nunca se descarten. ¿Por qué podrían lanzar de todos modos?

+0

Algún tipo de descarga (u otra E/S) es el motivo habitual. –

1

Porque ha preguntado sobre patrón de diseño Voy a decir qué regla de oro utilizo con éxito. Todas las funciones que tienen funcionalidad de limpieza/terminación deben NUNCA lanzar.

nombres Esas funciones son generalmente bien reconocidos:

  • Claro *()
  • Clean *()
  • Terminar *()
  • Destruye *()
  • de lanzamiento *()
  • Separar *()
  • * gratis()
  • Borrar *()
  • ~ Destructor()
  • ...

La lógica que se encuentra detrás de esto es que:

  • tiene que por lo menos 1 por cada función de recurso que garantías ese recurso específico se borra
  • (de forma recursiva) si se construye limpio- y libera múltiples recursos luego usa solo funciones que garantizan que estos recursos serán liberados de manera segura de excepción
  • programadores que usan yo ur código debe tener manera de limpiar los recursos
  • si exporta funciones de limpieza fuera de la biblioteca y luego no se propagan excepción (ya que los usuarios de la biblioteca no sabrán si se libera recursos)

Y yo Es posible intentar usar RAII pattern. Pero incluso en este caso, las reglas anteriores estarán en uso.

Cuestiones relacionadas