#1 Le 05/06/2010, à 16:08
- helly
[resolu]C++ question de base à propos des exceptions
Bonjour tout le monde !
Je suis entrain d'apprendre la gestion des exceptions en c++, pour ça j'ai créé une classe (bidon) Carre.
Le constructeur prend en argument un int qui détermine la taille du côté du carré, j'ai mis une exception dans le cas où ce coté fait 0 :
Carre::Carre(int c)
{
cote = c;
if (c == 0)
throw 0;
}
(Cette classe contiend aussi une méthode getPerimetre … … …qui affiche le périmètre )
Et donc ensuite j'ai tapé mon main :
…
try { Carre car(20);}
catch (int e) { std::cout<<"erreur\n";}
//suite du code
std::cout<<"perimètre:"<<car.getPerimetre()<<'\n';
return 0;
Et quand je compile il me met :
main.cpp:11: error: ‘car’ was not declared in this scope
Un forumeur m'a dit que c'est peut être du au fait que l'objet car est déclaré dans les accolades du try, ce qui fait que l'objet est détruit à la sortie de ce bloc, mais dans ce cas : comment faire une gestion d'erreur sur un constructeur ?
Merci à ceux qui peuvent m'éclairer !(ou qui au moins essayeront )
Dernière modification par helly (Le 06/06/2010, à 17:56)
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#2 Le 05/06/2010, à 16:18
- geenux
Re : [resolu]C++ question de base à propos des exceptions
J'avais répondu dans le tdcct, mais je remet ici
Il y a peut être en effet de vrai dans ce que tu dis pour ta première remarque à propos des accolades, mais dans ce cas ça veut dire qu'on peut pas faire de gestion exceptions dans un constructeur… je trouve ça un peu bizarre
Tu peux en effet déclencher sans souci une exception dans le constructeur. Dans ce cas, comme d'habitude, l'erreur est remontée jusqu'à ce qu'un bloque catch la rattrape. Le problème d'un déclenchement d'erreur dans le constructeur, c'est de savoir comment gérer le problème. Le constructeur est là pour créer la classe, si il y a une erreur qui se produit, il ne faut pas utiliser la classe qu'on a voulu créer. Donc ton code donné plus haut ne fonctionne pas, et s'il le faisait, son comportement ne serait pas définit. Quand tu lance l'exception avec throw, la création de la classe s'arrête, le constructeur n'est pas exécuté entièrement. Donc si tu pouvais utiliser la classe, ça voudrait dire que tu utilise une classe pas totalement créée.
Sinon grim7reaper a raison pour les blocs. Tu ne peux pas utiliser ce que tu as définit dans le bloc try dans le bloc catch. Tout ce qui est définit dans un bloc, c'est à dire un espace délimité par des crochets, est détruit à la fin du crochet fermant (dans le cas des pointeurs, le pointeur est détruit, mais le contenu du pointeur reste).
Les exceptions permettent de te donner la possibilité de passer un objet qui représente l'erreur (ou une variable d'un type de base, comme ton int par exemple).
Ça veut dire qu'il faudrait que dans le constructeur, tu lance une exeption avec une classe définie pour l'erreur (ou un type de base) que tu veux, et que tu rattrape cet objet et que tu l'utilise.
Exemple
try { Carre car(20);}
catch (int perimetre) { std::cout <<"plop\n";}
std::cout<<"perimetre:"<<perimetre<<'\n';
Et tu laisse ton constructeur tel quel.
throw 0 : lance une exception de type int (tu pourrais utiliser n'importe quelle classe ou type de base ici)
try { ... } : essaye de faire ce qui est entre accolades, dès que tu dépasse l'accolade fermante tout ce qui est dedans ce bloc est détruit
catch (int perimetre) { ... } : récupère l'erreur que tu as lancé dans la classe. Ici perimetre vaudra 0 puisque tu as fait throw 0.
Personnellement, je te conseille de définir une classe de base pour les erreurs, par exemple qui hérite de std::exception, histoire d'avoir une classe de base pour gérer les erreurs. Ensuite, quand tu as des erreurs plus spécifique, tu crée une autre classe héritée...
Dernière modification par geenux (Le 05/06/2010, à 16:28)
Hors ligne
#3 Le 05/06/2010, à 16:26
- helly
Re : [resolu]C++ question de base à propos des exceptions
Ok j'ai compris !!!!
Merci !
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#4 Le 05/06/2010, à 16:54
- Link31
Re : [resolu]C++ question de base à propos des exceptions
Sinon, j'aurais plutôt utilisé un assert() à la place du throw dans le constructeur. À condition évidemment que le paramètre "c" ne provienne pas directement ou indirectement d'une entrée non-sûre (utilisateur, fichier, réseau...).
Hors ligne
#5 Le 06/06/2010, à 05:07
- Le Farfadet Spatial
Re : [resolu]C++ question de base à propos des exceptions
Salut à tous !
Le choix entre assertion et exception est toujours délicat. Pour ma part, d'une manière générale, j'utilise les assertions pour me prémunir d'une erreur de programmation et les exceptions pour me prémunir des erreurs d'utilisations.
Cela dit, je pense que l'objectif est surtout d'apprendre à utiliser les exceptions. La réponse de Geenux est très complète pour expliquer le pourquoi de l'erreur. Cela dit, dans la mesure où tu apprends les exceptions, je te conseille d'utiliser une structure de classe. Il y a plusieurs raisons à cela.
Tout d'abord, cela te permettra de gérer les exceptions de manière plus fine. En effet, si toutes tes exceptions sont des entiers, alors des blocs « catch » capturerons toutes les exceptions, y compris celles que tu ne veux pas capturer.
Ensuite, utiliser des classes te permettra de transmettre des informations au moment de la capture, ce qui permettra d'avoir un diagnostic plus fin de l'erreur.
Enfin, cela te permettra de regrouper les différentes exceptions par héritage, pour pouvoir gérer des ensembles d'exceptions.
Après cette théorie, quelques exemples.
Dans ton cas très simple, cette classe suffit :
// Exception se déclenchant lorsque l'on tente de créer un carré de côté nul.
class CoteNul {};
Ce qui te permet de faire ceci dans le constructeur :
Carre::Carre (int c) {
if (c == 0) throw CoteNul ();
cote = c;
} // Carre::Carre (int)
Pour l'interception, tu peux procéder ainsi :
try {
Carre car (20);
std::cout << "périmètre : " << perimetre << '\n';
}
catch (const CoteNul &) {
std::cerr << "Tentative de créer un carré de côté nul.\n";
}
La bibliothèque standard propose une classe de base pour toutes les exceptions, la classe « std::exception » dans l'en-tête « <exception> ». Elle nécessite de définir la fonction membre « what », qui renseigne sur l'exception qui vient d'avoir lieu :
#include <exception>
// Exception se déclenchant lorsque l'on tente de créer un carré de côté nul.
class CoteNul: public std::exception {
public:
virtual const char *what () const throw () {
return "tentative de créer un carré de côté nul";
} // virtual const char *what () const
}; // class CoteNul
Alors, tu peux aussi bien n'intercepter que ton exception :
try {
Carre car(20);
std::cout << "périmètre : " << perimetre <<'\n';
}
catch (const CoteNul &e) {
std::cerr << "Erreur : " << e.what() << ".\n";
}
Que toutes les exceptions standards :
try {
Carre car(20);
std::cout << "périmètre : " <<perimetre<<'\n';
}
catch (const std::exception &e) {
std::cerr << "Erreur : " << e.what() << ".\n";
}
Admettons que chaque carré possède un identifiant entier, alors tu peux utiliser la classe pour transmettre cette information :
#include <exception>
// Exception se déclenchant lorsque l'on tente de créer un carré de côté nul.
class CoteNul: public std::exception {
private:
unsigned identifiant;
public:
CoteNul (unsigned id_) throw () {identifiant = id_;}
unsigned id () const throw () {return identifiant;}
virtual const char *what () const throw () {
return "tentative de créer un carré de côté nul";
} // virtual const char *what () const
}; // class CoteNul
Lorsque tu déclenches l'exception, il faut alors renseigner :
Carre::Carre (int c) {
// Ici, du code pour attribuer l'identifiant au carré.
if (c == 0) throw CoteNul (id);
cote = c;
} // Carre::Carre (int)
Ton message d'erreur peut alors fournir des informations :
try {
Carre car(20);
std::cout << "périmètre : "<< perimetre <<'\n';
}
catch (const CoteNul &e) {
std::cerr << "Erreur : tentative de créer un carré d'identifiant " << e.id() << " et de coté nul.\n";
}
Enfin, tu peux structurer tes exceptions à l'aide de l'héritage :
#include <exception>
// Classe mère pour la gestion des exceptions dues aux carrés.
// Attention : classe purement virtuelle, ne pas créer d'instance de cette classe.
class ErreurCarre: public std::exception {
public:
virtual const char *what () const throw () = 0;
} // class ErreurCarre
// Exception se déclenchant lorsque l'on tente de créer un carré de côté nul.
class CoteNul: public ErreurCarre {
public:
virtual const char *what () const throw () {
return "tentative de créer un carré de côté nul";
} // virtual const char *what () const
}; // class CoteNul
// Exception se déclenchant lorsque l'on tente de créer un rectangle au lieu d'un carré.
class CarreRectangle: public ErreurCarre {
public:
virtual const char *what () const throw () {
return "tentative de créer un rectangle plutôt qu'un carré";
} // virtual const char *what () const
}; // class CarreRectangle
Alors, tu peux n'intercepter que l'une des exceptions ou faire des blocs distinct pour chaque exception :
try {
Carre car(20);
std::cout << "périmètre : "<< perimetre <<'\n';
}
catch (const CoteNul &e) {
std::cerr << "Erreur : tentative de créer un carré de coté nul.\n";
}
catch (const CarreRectangle &e) {
std::cerr << "Erreur : tentative de créer un carré rectangulaire.\n";
}
Ou traiter toutes les exceptions venant des carrés en une seule fois :
try {
Carre car(20);
std::cout << "périmètre : "<< perimetre <<'\n';
}
catch (const ErreurCarre &e) {
std::cerr << "Erreur : " << e.what() << ".\n";
}
Donc, si tu m'en crois, je te conseille d'utiliser des classes pour gérer les exceptions, c'est à la fois souple et puissant.
Comme d'habitude, la lecture du Stroustrup est obligatoire, puisque tout y est expliqué en détails.
À bientôt.
Le Farfadet Spatial
Dernière modification par Le Farfadet Spatial (Le 06/06/2010, à 05:10)
Hors ligne
#6 Le 06/06/2010, à 10:40
- helly
Re : [resolu]C++ question de base à propos des exceptions
Ok merci beaucoup, je vais me pencher là dessus
C'est justement après la lecture de Stroustrup que je me posais ces question, malheureusement je trouve que ce bouquin n'est pas toujours très clair…(avis personnel)
En tous cas merci !
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#7 Le 06/06/2010, à 10:59
- helly
Re : [resolu]C++ question de base à propos des exceptions
Oh chose bizarre !
J'essaye tout d'abord ta technique à propos de la classe CoteNul{} (pas celle qui dérive de exception,mais celle de ton premier exemple)
Je fais donc un Carre.h :
#include <iostream>
class Carre
{
//pour exception
class CoteNul{};
private : int cote;
public : Carre(int);
};
Puis Carre.cpp
#include "Carre.h"
Carre::Carre(int c)
{
cote = c;
if (c == 0)
throw CoteNul();
}
Puis pour finir le main.cpp
#include "Carre.h"
int main()
{
try{ Carre car(20);}
catch(const CoteNul &c)
{ std::cout <<"erreur,le cote est nul\n";}
return 0;
}
Et là le compilateur me dit :
g++ main.cpp Carre.cpp -o m
main.cpp: In function ‘int main()’:
main.cpp:6: error: ISO C++ forbids declaration of ‘CoteNul’ with no type
main.cpp:6: error: expected ‘)’ before ‘&’ token
main.cpp:6: error: expected ‘{’ before ‘&’ token
main.cpp:6: error: ‘c’ was not declared in this scope
main.cpp:6: error: expected ‘;’ before ‘)’ token
Où ais je mal agit ? Il me seblait pourtant avoir recopié fidèlement ton exemple…
Dernière modification par helly (Le 06/06/2010, à 11:03)
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#8 Le 06/06/2010, à 17:42
- geenux
Re : [resolu]C++ question de base à propos des exceptions
Non, tu n'as pas déclaré la classe côte nul au bon endroit.
Tu as mis la déclaration de la classe à l'intérieur d'une autre classe. En java c'est accepté il me semble, mais ça ne se fait pas en c++.
Il faut que tu définisse la classe avant :
#include <iostream>
//pour exception
class CoteNul{};
class Carre
{
private : int cote;
public : Carre(int);
};
Hors ligne
#9 Le 06/06/2010, à 17:48
- helly
Re : [resolu]C++ question de base à propos des exceptions
je test…
edit : ok ça marche très bien comme ça
Mais c'est «légal» de mettre un truc comme ça dans le fichier Carre.h ? faudrait pas créer un fichier exprès pour ça?
Y'a une norme là dessus ?
(pour ta référence à Java j'en sais rien, j'en ai jamais fait ^^')
Dernière modification par helly (Le 06/06/2010, à 17:51)
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#10 Le 06/06/2010, à 18:02
- helly
Re : [resolu]C++ question de base à propos des exceptions
C'est pourtant bizarre, dans un exemple de class Stack; Stroustrup déclare les classes pour les erreurs dans la class Stack :
class Stack
{
char *v;
int top;
int max_size;
public:
class Underflow{};
…
};
Je me suis dit que ça marchait peut être par ce qu'il a déclaré cette classe d'erreur en public alors que dans mon premier exemple je la déclarais en privée, mais non, ça change rien ça passe pas quand même.
Le seul moyen d'y arriver est la méthode de geenux → déclarer la classe d'erreur en dehors de la classe Carre…
OK j'ai une méthode qui marche, mais je me demande pourquoi celle de Stroustrup marche pas pour mon cas
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#11 Le 06/06/2010, à 18:52
- Link31
Re : [resolu]C++ question de base à propos des exceptions
Non, tu n'as pas déclaré la classe côte nul au bon endroit.
Tu as mis la déclaration de la classe à l'intérieur d'une autre classe. En java c'est accepté il me semble, mais ça ne se fait pas en c++.
Non. En C++, on peut définir une classe dans une autre classe, ou en dehors (dans le même fichier ou pas), au choix. Alors qu'en Java on est obligé de définir une "sous-classe" à l'intérieur d'une autre classe (sous certaines conditions), ou d'utiliser un fichier séparé.
@helly : pour accéder à la classe Underflow depuis l'extérieur (dans ton dernier exemple), tu dois écrire : Stack::Underflow.
Par exemple : "throw Stack::Underflow()" (ou "throw Underflow()" si tu es déjà dans Stack), et "catch (const Stack::Underflow& e)".
Hors ligne
#12 Le 06/06/2010, à 18:57
- geenux
Re : [resolu]C++ question de base à propos des exceptions
Non. En C++, on peut définir une classe dans une autre classe, ou en dehors (dans le même fichier ou pas), au choix. Alors qu'en Java on est obligé de définir une "sous-classe" à l'intérieur d'une autre classe (sous certaines conditions), ou d'utiliser un fichier séparé.
Merci de la précision. Je n'avais jamais vu une telle déclaration de classe en c++. (quoique il me semble que j'ai déjà vu une structure dans une classe, donc finalement ça parait logique qu'on puisse aussi mettre une classe).
Hors ligne
#13 Le 06/06/2010, à 19:49
- helly
Re : [resolu]C++ question de base à propos des exceptions
@helly : pour accéder à la classe Underflow depuis l'extérieur (dans ton dernier exemple), tu dois écrire : Stack::Underflow.
Par exemple : "throw Stack::Underflow()" (ou "throw Underflow()" si tu es déjà dans Stack), et "catch (const Stack::Underflow& e)".
Ha ok, merci
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne
#14 Le 08/06/2010, à 02:18
- Le Farfadet Spatial
Re : [resolu]C++ question de base à propos des exceptions
Salut à tous !
Bon, voilà : j'étais loin du forum, donc il y a eu des réponses complètes. Juste une chose :
Mais c'est «légal» de mettre un truc comme ça dans le fichier Carre.h ? faudrait pas créer un fichier exprès pour ça?
Y'a une norme là dessus ?
À ma connaissance, il n'y a pas de norme internationale de type ISO concernant l'organisation d'un code source, parce que ce n'est pas le propos. En revanche, un projet bien mené définit ses pratiques de codages -- on peut citer celles du projet GNU (surtout pour le C) ou celles du projet KDE, par exemple --, qui, entre autre, vont définir si les exceptions doivent être déclarées dans une classe ou non.
Pour ma part, dans la mesure où je ne mets pas tout dans les classes, je ne définis pas les exceptions dans les classes. Généralement, je crée des espaces de nommages à part.
Cette question rejoins le difficile problème de l'organisation d'un code source, qui ne se résout que par l'expérience et les tests.
À bientôt.
Le Farfadet Spatial
Hors ligne
#15 Le 08/06/2010, à 10:17
- helly
Re : [resolu]C++ question de base à propos des exceptions
Ok, touka merci à tous, voila une chose de plus que j'ai comprise
Archlinux-wmii-dwb.
Un problème résolu ? Faites le savoir en mettant [résolu] à côté du titre de votre topic.
Un problème non résolu ? Faites le savoir en insultant ceux qui cherchent à vous aider.
Un site bleu super remasterised©, un wiki cherchant des volontaires pour traduire un site.
Hors ligne