Question

Possible en double: Quand un bloc try fonction utile?
entre syntaxe try-catch pour la fonction

Ce code renvoie une exception int tout en construisant l'objet Dog à l'intérieur UseResources de classe. L'exception int est interceptée par un bloc de try-catch normal et les sorties de code:

Cat()  
Dog()  
~Cat()  
Inside handler

#include <iostream>
using namespace std;

class Cat
{
    public:
    Cat() { cout << "Cat()" << endl; }
    ~Cat() { cout << "~Cat()" << endl; }
};

class Dog
{
    public:
    Dog() { cout << "Dog()" << endl; throw 1; }
    ~Dog() { cout << "~Dog()" << endl; }
};

class UseResources
{
    class Cat cat;
    class Dog dog;

    public:
    UseResources() : cat(), dog() { cout << "UseResources()" << endl; }
    ~UseResources() { cout << "~UseResources()" << endl; }
};

int main()
{
    try
    {
        UseResources ur;
    }
    catch( int )
    {
        cout << "Inside handler" << endl;
    }
}

Maintenant, si l'on remplace la définition du constructeur UseResources(), avec celui qui utilise un function try block, comme ci-dessous,

UseResources() try : cat(), dog() { cout << "UseResources()" << endl; } catch(int) {}

la sortie est identique

Cat()  
Dog()  
~Cat()  
Inside handler 

i.e.., Avec exactement le même résultat final.

Quelle est donc, dans le but d'un function try block?

Était-ce utile?

La solution

Imaginez si UseResources a été défini comme suit:

class UseResources
{
    class Cat *cat;
    class Dog dog;

    public:
    UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
    ~UseResources() { delete cat; cat = NULL; cout << "~UseResources()" << endl; }
};

Si Dog::Dog() lancers francs, puis cat sera fuite de mémoire. Becase constructeur de UseResources jamais terminé , l'objet n'a jamais été entièrement construit. Et donc il n'a pas son destructor appelé.

Pour éviter cette fuite, vous devez utiliser un essai au niveau de la fonction / bloc catch:

UseResources() try : cat(new Cat), dog() { cout << "UseResources()" << endl; } catch(...)
{
  delete cat;
  throw;
}

Pour répondre à votre question plus en détail, dans le but d'un essai au niveau de la fonction / bloc catch dans les constructeurs est spécifiquement pour faire ce genre de nettoyage. essai niveau de fonction / blocs catch ne peut pas exceptions (Swallow les réguliers peuvent). S'ils attrapent quelque chose, ils vont le jeter à nouveau quand ils atteignent la fin du bloc catch, à moins que vous rethrow explicitement avec throw. Vous pouvez transformer un type d'exception dans un autre, mais vous ne pouvez pas avaler et continuer comme ça n'a pas eu lieu.

Ceci est une autre raison pour laquelle les valeurs et les pointeurs intelligents doivent être utilisés à la place des pointeurs nus, même en tant que membres de la classe. Parce que, comme dans votre cas, si vous avez juste des valeurs membres au lieu de pointeurs, vous ne devez pas le faire. Il est l'utilisation d'un pointeur nu (ou toute autre forme de ressources non gérée dans un objet RAII) que les forces ce genre de chose.

Notez que c'est à peu près l'utilisation légitime d'essayer de fonction / blocs catch.


Plus de raisons de ne pas utiliser la fonction blocs try. Le code ci-dessus est subtilement cassé. Considérez ceci:

class Cat
{
  public:
  Cat() {throw "oops";}
};

Alors, ce qui se passe dans le constructeur de UseResources? Eh bien, le new Cat d'expression va jeter, évidemment. Mais cela signifie que cat jamais lors de leur initialisation. Ce qui signifie que delete cat produira un comportement non défini.

Vous pouvez essayer de corriger cela en utilisant un complexe lambda au lieu de simplement new Cat:

UseResources() try
  : cat([]() -> Cat* try{ return new Cat;}catch(...) {return nullptr;} }())
  , dog()
{ cout << "UseResources()" << endl; }
catch(...)
{
  delete cat;
  throw;
}

qui fixe théoriquement le problème, mais il casse un invariant présumé de UseResources. A savoir, cette volonté de UseResources::cat à tout moment un pointeur valide. Si tel est un invariant de UseResources, alors ce code échouera car elle permet la construction de UseResources malgré l'exception.

En fait, il n'y a pas moyen de rendre ce code en toute sécurité, à moins new Cat est noexcept (soit explicitement ou implicitement).

En revanche, cela fonctionne toujours:

class UseResources
{
    unique_ptr<Cat> cat;
    Dog dog;

    public:
    UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
    ~UseResources() { cout << "~UseResources()" << endl; }
};

En bref, regard sur une fonction de niveau bloc try comme grave odeur de code.

Autres conseils

blocs try fonction ordinaires ont relativement peu d'utilité. Ils sont presque identiques à un bloc d'essai à l'intérieur du corps:

int f1() try {
  // body
} catch (Exc const & e) {
  return -1;
}

int f2() {
  try {
    // body
  } catch (Exc const & e) {
    return -1;
  }
}

La seule différence est que la fonction bloc try vit dans la fonction portée un peu plus grande, tandis que la seconde vie de construction dans la fonction corps-portée - l'ancien périmètre ne voit que les arguments de la fonction, celle-ci aussi locale les variables (mais cela n'affecte pas les deux versions de blocs try).

La seule application intéressante est dans un constructeur -Essayer-block:

Foo() try : a(1,2), b(), c(true) { /* ... */ } catch(...) { }

Ceci est la seule façon que les exceptions à l'un des initialiseurs peuvent être capturés. Vous ne pouvez pas poignée l'exception, puisque la construction entière de l'objet doit encore échouer (donc vous devez quitter le bloc catch avec une exception, si vous voulez ou non). Cependant, il la seule façon de gérer les exceptions dans la liste initialiseur spécifiquement.

Est-ce utile? Probablement pas. Il y a pratiquement pas de différence entre un bloc d'essai constructeur et ce qui suit, plus typique "initialize à nul et assign" modèle, qui lui-même est terrible:

Foo() : p1(NULL), p2(NULL), p3(NULL) {
  p1 = new Bar;
  try {
    p2 = new Zip;
    try {
      p3 = new Gulp;
    } catch (...) {
      delete p2;
      throw;
    }
  } catch(...) {
    delete p1;
    throw;
  }
}

Comme vous pouvez le voir, vous avez un désordre ingérable, infranchissable. Un constructeur-bloc try serait encore pire parce que vous pourriez même pas dire combien des pointeurs ont déjà été attribués. Alors, vraiment, il est que utile si vous avez précisément deux allocations leakable Mise à jour:. Merci à la lecture cette question j'ALERTÉS le fait qui en fait vous ne pouvez pas utiliser le bloc catch pour nettoyer les ressources du tout, car faisant référence à des objets membre est un comportement non défini. Donc, [mise à jour de fin]

En bref:. Il est inutile

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top