Partager via


Comment : Conception de la sécurité d'exception

Un des avantages du mécanisme d'exceptions est que l'exécution, ainsi que des données sur l'exception, branche directement à partir de l'instruction qui lève l'exception à la première instruction catch qui gère -le.Le gestionnaire peut être un certain nombre de niveaux dans la pile des appels.Fonctions qui sont appelées entre l'instruction de test et l'instruction throw ne sont requises pour connaître rien sur l'exception qui est levée.Toutefois, elles doivent être conçus afin qu'ils puissent sortie de la portée « de » à tout moment où une exception peut se propager de sous, puis fassent faire sans laisser les objets, la mémoire perdue, ou les structures de données qui sont partiellement créée dans les rapports inutilisables.

Techniques de base

Une stratégie fiable de gestion des exceptions requiert la attentionnée pensée et doit faire partie du processus de conception.La plupart des exceptions sont détectées et en général levées aux couches inférieures d'un package logiciel, mais généralement ces couches n'ont pas suffisamment de contexte pour gérer l'erreur ou pour exposer un message aux utilisateurs finaux.Dans les couches moyennes, les fonctions peuvent intercepter et lever de nouveau une exception lorsqu'elles doivent examiner l'objet exception, ou elles ont des informations utiles supplémentaires à prévoir le niveau supérieur qui intercepte finalement l'exception.Une fonction doit intercepter et « avaler » une exception uniquement s'il peut récupérer entièrement de celle-ci.Dans de nombreux cas, le comportement correct dans les couches moyennes est de laisser la propagation des exceptions en haut de la pile des appels.Même à la couche élevé, il peut s'avérer nécessaire de conserver une exception non gérée exécuter un programme si l'exception de le programme dans un état dans lequel l'exactitude ne peut pas être garanti.

Quelle que soit la façon dont une fonction gère une exception, pour garantir que c'est sécurisée du point « , » elle doit être effectué en fonction de les principes de base suivants.

Hh279653.collapse_all(fr-fr,VS.110).gifMaintenez les classes de ressources simples

Lorsque vous encapsulez la gestion des ressources manuelle dans les classes, utilisez une classe qui fait rien d'autre gérer chaque ressource ; sinon, vous pouvez introduire des fuites.

Hh279653.collapse_all(fr-fr,VS.110).gifUtilisez l'idiome RAII pour gérer des ressources

Pour être sécurisée du point de vue, une fonction doit garantir que les objets qu'elle a alloué à l'aide de malloc ou d' new sont détruits, et toutes les ressources telles que les handles de fichiers sont fermées ou libérées même si une exception est levée.La saisie de ressource est d'initialisation gestion de liens de l'idiome de (RAII) de ces ressources au parcours de vie des variables automatiques.Lorsqu'une fonction est hors de portée, en retournant normalement ou à cause d'une exception, les destructeurs pour toutes les variables automatiques entièrement construites sont appelés.Un objet de wrapper RAII tel qu'un pointeur intelligent appelle la fonction DELETE appropriée ou de fin dans son destructeur.Dans du code sécurisé du point, il est critique en important de passer la propriété de chaque ressource immédiatement à un certain type d'objet RAII.Notez qu' vector, string, make_shared, fstream, et les classes similaires gèrent la saisie de ressources pour vous. Toutefois, unique_ptr et les constructions traditionnelles d' shared_ptr sont spéciaux parce que la saisie de ressource est exécutée par l'utilisateur au lieu de l'objet ; par conséquent, elles comptent comme la version finale de ressource est destruction mais sont incertains comme RAII.

Les trois garanties d'exception

En général, la sécurité d'exceptions est décrite en termes de trois garanties d'exception qu'une fonction peut fournir : la garantie de -- sans échec, la garantie forte, et la garantie de base.

Hh279653.collapse_all(fr-fr,VS.110).gifGarantie de -- sans échec

Garantie de -- sans échec (ou, « -- sans clause throw ») est la garantie la plus forte qu'une fonction peut fournir.Elle indique que la fonction ne lève pas d'exception ou ne permet pas un à la propagation.Toutefois, vous ne pouvez pas de manière fiable fournir une telle garantie à moins que (a) vous sachent que toutes les fonctions que les appels à cette fonction sont également -- sans échec, ou (b) vous savent que toutes les exceptions levées soient interceptées avant qu'elles n'atteignent cette fonction, ou (c) vous savent intercepter et gérer correctement les exceptions qui peuvent atteindre cette fonction.

La garantie forte et la garantie de base s'appuient sur l'hypothèse que les destructeurs sont -- sans échec.Tous les conteneurs et les types de la garantie standard de bibliothèque que les destructeurs ne lèvent pas.Il existe également une spécification inverse : La bibliothèque standard requiert que les types définis par l'utilisateur qui sont fournis à TI- pour l'exemple, comme le modèle argument- doit être non lever des destructeurs.

Hh279653.collapse_all(fr-fr,VS.110).gifGarantie forte

La garantie forte indique que si une fonction est hors de portée en raison d'une exception, elle ne coulera pas la mémoire et état de votre programme ne sera pas modifié.Une fonction qui fournit une garantie forte est essentiellement une transaction qui comporte la sémantique de validation ou de restauration : il réussit complètement il n'a aucun effet.

Hh279653.collapse_all(fr-fr,VS.110).gifGarantie de base

La garantie de base est la plus basse des trois.Toutefois, il peut être le meilleur choix lorsqu'une garantie forte est trop coûteuse dans la consommation de mémoire ou des performances.La garantie de base indique que si une exception se produit, aucune mémoire n'est coulée et l'objet est toujours dans un état utilisable si les données ont pu être modifiées.

Classes sécurisé du point

Une classe peut aider à garantir sa propre sécurité d'exception, même lorsqu'elle est consommée par les fonctions potentiellement dangereuses, en empêchant d'être partiellement construit ou partiellement détruit.Si un constructeur de classe s'arrête avant l'achèvement, l'objet n'est jamais créé et son destructeur ne sera jamais appelé.Bien que les variables automatiques qui sont initialisées avant l'exception aient leurs destructeurs la mémoire ou les ressources appelée et allouée dynamiquement qui ne sont pas gérées par un pointeur intelligent ou une variable automatique est semblable coulée.

Les types intégrés sont tout le -- sans échec, et les types standard de bibliothèque prennent en charge la garantie de base minimale.Suivez ces indications pour tout type défini par l'utilisateur qui doit être sécurisée du point de vue des exceptions :

  • Utilisez les pointeurs intelligents ou d'autres wrappers de type RAII pour gérer toutes les ressources.Évitez les fonctionnalités de gestion des ressources dans votre destructeur de classe, parce que le destructeur ne sera pas appelé si le constructeur lève une exception.Toutefois, si la classe est un gestionnaire de ressources dédié qui vérifie qu'une ressource, il est acceptable d'utiliser le destructeur pour gérer des ressources.

  • Incluez qu'une exception levée dans un constructeur de classe de base ne peut pas être avalée dans un constructeur de classe dérivée.Si vous souhaitez traduire et l'exception générale de la classe de base dans un constructeur dérivé, utilisez un bloc try de fonction.Pour plus d'informations, consultez Comment : Gérer les exceptions dans les constructeurs de classe de base (C++).

  • Déterminez s'enregistrer tout l'état de classe dans une donnée membre qui est encapsulée dans un pointeur intelligent, surtout si une classe a un concept de « initialisation qui est autorisée à échouer. » Bien que C++ tienne compte des données membres non initialisées, il ne prend pas en charge les instances de classe non initialisées ou partiellement initialisées.Un constructeur doit le succès ou l'échec ; aucun objet n'est créé si le constructeur n'exécute pas à l'achèvement.

  • N'autorisez à aucune exception afin de créer une séquence d'échappement d'un destructeur.Un axiome de base C++ est que les destructeurs ne doivent jamais permettre à une exception pour propager vers le haut de la pile des appels.Si un destructeur doit effectuer exception- potentiellement lever l'exécution, il doit le faire dans un bloc catch de test et avaler l'exception.La bibliothèque standard fournit cette cohérence sur les destructeurs qu'il définit.

Voir aussi

Concepts

Erreurs et gestion des exceptions C++ (moderne)

Comment : Interface entre le code exceptionnel et non Exceptionnel