2010-10-14 5 views
6

Dans mon destructeur, je dois nettoyer quelques ressources. Disons que j'ai trois appels pour effacer les ressources qui pourraient jeter. Puisqu'il n'est pas bon de laisser une exception laisser un destructeur, quel devrait être mon motif de conception? Apparemment, le chemin ci-dessous n'est pas évolutif.Exception dans le destructeur

Merci.

class B::~B(){ 

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

Il est * définitivement * non évolutif. En essayant de gérer seulement trois ressources, vous avez déjà des erreurs de logique. – nobar

Répondre

2

quoi que ce soit de capture qui peut jeter dans votre destructor avec un fourre-tout (à savoir, catch (...)) et faire de votre mieux pour gérer les exceptions lancées. Assurez-vous qu'aucune exception ne se propage hors du destructeur, ce que le fourre-tout vous aidera à empêcher.

10

Pourquoi ne pas:

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

Offtop: Comment puis-je appliquer l'indentation? –

+0

Utilisez le bouton "code", pas les ticks de retour. Le bouton de code a un 101010 dessus. Tapez votre code (en retrait), sélectionnez le code, puis appuyez sur le bouton de code. – Starkey

+0

@Starkey: Malheureusement, il n'y a pas ces boutons ni l'aperçu. J'ai demandé sur meta mais toujours en attente de réponse –

5

encapsulent chaque ressource dans une classe qui les efface dans son destructor (avec un essai entourant/catch):

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; 
} 

A shared_ptr avec une coutume Deleter est un moyen d'y parvenir sans avoir à créer une classe entière pour chaque type de ressource.

Vous pouvez améliorer l'élimination de l'exception (consigner le problème, par exemple), en fonction de ce qui est levé.

Encore mieux, modifiez les ressources A, B et C pour qu'elles se libèrent elles-mêmes de leurs propres destructeurs. Cela pourrait ne pas être possible, cependant. Dans les deux cas, vous pouvez placer autant de ressources de ce type dans une seule classe que vous le souhaitez, sans ajouter de code au destructeur de la classe. C'est "scalabilité". Tout le point de RAII, est que chaque utilisateur d'une ressource ne devrait pas avoir à écrire du code de nettoyage afin d'utiliser la ressource correctement.

+1

Eh bien sorte de. Le but de RAII est que le nettoyage soit fait dans vos destructeurs, donc vous n'appelez pas delete ou freeHandle ou toute autre fonction dans les corps de votre code. Le problème principal ici est que le code de nettoyage ne devrait généralement pas être utilisé en premier lieu. – CashCow

+0

@CashCow: "le nettoyage est tout fait dans vos destructeurs" - eh bien, si les ressources sont conçues avec les principes RAII, alors le nettoyage est fait dans le destructeur de la ressource. Si je dois écrire moi-même la classe RAII, il est vrai que c'est mon destructeur. Mais quel que soit celui qui écrit la ressource RAII, les utilisateurs de cette ressource RAII n'ont pas besoin d'écrire ou d'appeler explicitement le code de nettoyage. C'est ce que je voulais dire par "utilisateurs". –

+0

merci, je devenais fou en regardant les autres suggestions:/ –

2

Vous pouvez également envelopper vos fonctions ClearResources pour vous assurer qu'elles ne seront jamais lancées. Pourquoi pourraient-ils jeter quand même?

+0

Une sorte de flush (ou autre E/S) est la raison habituelle. –

1

Parce que vous avez demandé à propos de motif de conception Je vais dire quelle règle de base j'utilise avec succès. Toutes les fonctions qui ont une fonctionnalité de nettoyage/terminaison doivent NE JAMAIS lancer.

Ces fonctions sont généralement bien des noms reconnus:

  • Effacer *()
  • Clean *()
  • Terminate *()
  • Destroy *()
  • de * de sortie()
  • Détacher *()
  • Gratuit *()
  • Effacer *()
  • ~ Destructeur()
  • ...

La raison qui se cache derrière cela est que:

  • il doit au moins 1 fonction par ressource qui garantit cette ressource spécifique est effacée
  • (récursive) si vous construisez Clean- up fonction et vous libérez plusieurs ressources puis utilisez uniquement les fonctions qui garantissent que ces ressources seront libérées en toute sécurité façon
  • programmeurs qui utilise yo ur code doit avoir moyen de si vous exportez des fonctions de nettoyage à l'extérieur de la bibliothèque dépolluer les ressources
  • alors ne se propagent pas exception (parce que les utilisateurs de la bibliothèque ne sauront pas si la ressource est libérée)

Et je il est possible d'essayer d'utiliser RAII pattern. Mais même dans ce cas ci-dessus les règles seront en usage.

Questions connexes