C++ : ​La gestion des erreurs avec les exceptions

Comprendre la classe sans garantie

Testez gratuitement nos 1324 formations

pendant 10 jours !

Tester maintenant Afficher tous les abonnements
À travers un exemple concret, identifiez les habitudes de programmation qui rendent une classe et ses membres sensibles aux exceptions.
06:06

Transcription

Maintenant que l'on connaît les différents niveaux de tolérance aux exceptions des classes, il faut maintenant pouvoir identifier le niveau de tolérance d'une classe. Alors ici, j'ai une classe Mots Croises qui permet de gérer une grille de mots croisés comme ici, avec des étoiles à la place des cases noires et puis une méthode affichée qui permet de parcourir cette grille qui est reçue en paramètre par valeur et ensuite donc de parcourir chaque ligne et pour chaque ligne, de parcourir chaque mot ici, et d'afficher les mots séparés par un dièse. Donc à l’exécution, voilà ce que cela donne, l'affichage de ma grille. On va s'intéresser à la classe elle-même, la classe Mots Croises qui est assez simple. Elle a des accesseurs hauteur et largeur ici, la méthode ligne qui permet de la remplir, des constructeurs et des structeurs sur lesquels on s'attardera par la suite, la méthode mot qui permet de parcourir les mots d'une ligne et une méthode zerogrille qui va être très utile pour allouer au moment de l'initialisation de notre grille, pour allouer la mémoire pour les lignes notamment qui sont des tableaux à deux dimensions. Maintenant, on va se poser la question de sa garantie et de sa tolérance aux exceptions. La première question à se poser pour chaque méthode, c'est quelle exception peut être levée dans la méthode. La façon la plus simple est de chercher les throw. Les throw vont nous permettre de détecter facilement les exceptions levées. Alors on a des throw ici, sur mot et sur ligne. Alors mot est une méthode constante, ce qui signifie qu'on ne peut pas changer l'instance, modifier l'instance. Ce throw intervient en tout début alors qu'on manipule les entiers donc l'instance n'est pas en train d'être modifiée, impossible, le throw intervient, notre instance n'est pas remise en cause dans sa cohérence et il n'y a pas de fuite mémoire. Donc, on est sur une garantie forte pour cette méthode. Si on regarde le reste de la méthode, on a très peu d'appels de fonction, strlen, qui a priori ne lève pas d'exception sauf un EDC qui est interdit. Mais comme on valide l'indice, l'EDC, et que normalement, on fait attention au contenu de notre tableau, il n'y aura pas d'exception là-dessus. Et de toute façon, on a l'Opa de mémoire et on ne modifie pas notre classe. Donc aucun risque sur cette méthode. Sur ligne maintenant, qui fait aussi des throw, on a quasiment les mêmes réflexions, c'est-à-dire que ligne ne modifie pas tant qu'il vérifie les paramètres. Et donc au moment où il fait le throw, on n'a ni modification de notre classe ni allocation puisqu'on manipule le tableau existant et qu'on s'assure à chaque fois qu'on est bien entre 0 et taille, taille qui ne dépasse pas la largeur est qui est allouée à + 1. Ici, on le voit sur l'allocation largeur + 1. Donc ici, il n'y a pas non plus d'exception sensible et donc il n'y a pas de risque d'incohérence de notre classe. On continue à remonter. Ici, le destructeur est marqué noexcept. Au passage, ce mot-clé noexcept signifie qu'on ne lèvera pas d'exception sur le destructeur qui est recommandé en c++11. Il y a un underscore, ce qui n'est pas standard mais c'est pour avoir une macro qui permet de tolérer les anciennes versions de Visual Studio qui ne connaissent pas le noexcept. On définit donc le noexcept avec underscore et pour les compilateurs qui supportent le noexcept, on l'associe au vrai noexcept. Vous pouvez éventuellement changer dans le projet pour avoir quelque chose de plus standard par le noexcept mais cela fonctionnera aussi sur tout compilateur. Pour en revenir à notre destructeur, il n'y a pas de risque, delete ne lève pas d'exception surtout sur un char *. Si c'est sur une classe, normalement elle ne devrait pas dans son destructeur lever d'exception sauf si elle a été mal programmée. Donc à vérifier si vous avez des classes. Là, il n'y en a pas donc il n'y a pas de risque. Il reste donc nos constructeurs. Nos constructeurs ont une méthode qui peut lever une exception. C'est zerogrille. Pourquoi n'y a-t-il pas de throw ? Elle peut lever une exception parce qu'elle fait des allocations et que ces allocations peuvent échouer. Dans le cas d'un échec, on aura un bad_alloc, une allocation qui n'a pas pu aboutir du fait de l'absence de mémoire. D'autres exceptions pourraient survenir si on avait des champs qui étaient des classes mais ici, ce n'est ensuite que des types primitifs, entiers pour largeur et pour lignes. Il faut donc vraiment se focaliser sur la location qui est le cœur du problème ici, et essayer de tolérer les chaînes d'une allocation. Ce n'est pas l'objet ici. Ici, on va déjà constater ce problème. Donc imaginons que j'alloue ici de quoi stocker cette ligne et que cela se passe bien. Mais au bout de la troisième ligne, j'ai un échec de l'allocation. À ce moment-là, j'ai donc une exception qui est levée, mes trois premières allocations pour ce tableau plus les deux premières lignes ont été faites, ont été affectées mais ce champ disparaît puisque je quitte le zerogrille et le constructeur qui a appelé le zerogrille. Donc tout cela est perdu mais l'allocation par contre est restée au niveau du système. Donc on a une fuite mémoire en bon et du forme. On peut d'ailleurs le constater ici où j'ai surchargé les opérateurs New et delete pour pouvoir contrôler la mémoire allouée. Comme il est assez difficile de saturer la mémoire d'un programme avec la quantité de mémoire qu'on a maintenant, ici, je simule la saturation en provoquant une exception lorsque j'ai un MAX_MEMOIRE ici, j'ai pris 150 octets. Donc là, dans main, je capte ce bad_alloc pour afficher la fuite mémoire que j'ai en cas d'exception. Et donc ici j'ai 68 octets, l'allocation qui était en cours, qui n'est pas libérée. Donc ce sont les premières lignes qui avaient été recopiées qui ne sont pas libérées correctement. On a vu comment identifier par la lecture du code des parties sensibles. On va voir par la suite comment corriger ce problème pour permettre à notre classe de proposer une garantie forte aux exceptions.

C++ : ​La gestion des erreurs avec les exceptions

Profitez des nombreux atouts des exceptions pour gérer les erreurs dans vos développements C++. Apprenez à les déclencher, les intercepter, les personnaliser, etc.

56 min (13 vidéos)
Aucun commentaire n´est disponible actuellement
 
Logiciel :
Spécial abonnés
Date de parution :20 oct. 2016

Votre formation est disponible en ligne avec option de téléchargement. Bonne nouvelle : vous ne devez pas choisir entre les deux. Dès que vous achetez une formation, vous disposez des deux options de consultation !

Le téléchargement vous permet de consulter la formation hors ligne et offre une interface plus conviviale. Si vous travaillez sur différents ordinateurs ou que vous ne voulez pas regarder la formation en une seule fois, connectez-vous sur cette page pour consulter en ligne les vidéos de la formation. Nous vous souhaitons un excellent apprentissage avec cette formation vidéo.

N'hésitez pas à nous contacter si vous avez des questions !