IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Instruction catch et classes dérivées.


Sujet :

C++

  1. #1
    Membre actif
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Points : 233
    Points
    233
    Par défaut Instruction catch et classes dérivées.
    J'ai un ensembles de classes décrivant des exceptions qui peuvent être levées à chaque moment. Mon problèmes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    try {
        ...
        if(...) throw new DeriveeException(...);
        ...
    } catch(BaseException *ex) {
    }
    Quand je throw une exception avec une classe dérivée de la class de base dans le bloque catch, ce dernier n'est pas appelé. En applicant, le bloque catch(...), les exception "throwées" sont interceptées mais sans les informations que je passe à travers les classes.
    A ce stade du projet, je veux pas gaspiller du temps à coder plusieurs blocque catch un pour chaque type d'exception.

    Y a-t-il une solution pour intercepter plusieurs exceptions en interceptant la classe de base comme dans le langage C#

    Merci.

  2. #2
    Membre actif
    Étudiant
    Inscrit en
    Octobre 2007
    Messages
    189
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2007
    Messages : 189
    Points : 213
    Points
    213
    Par défaut
    Oui : il s'agit d'utiliser les références ( const ) .

    Pourquoi ça ne fonctionne pas avec les pointeurs ? Mon hypothèse c'est que T (const) * est trop différent de T alors que T (const) & est quasi similaire à T. Est-ce correcte ?

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    57
    Détails du profil
    Informations personnelles :
    Âge : 16
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 57
    Points : 65
    Points
    65
    Par défaut
    Ca marche avec les pointeurs!

    Le code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <iostream>
     
    using namespace std;
     
    class A {};
    class B : public A{};
     
    int main()
    {
        try
        {
            throw new B();
        }catch (A*)
        {
            cout << "..." << endl;
        } catch (B*)
        {
            cout << "!!!" << endl;
        }
        return 0;
    }
    me donne même un warning:

    C:\Users\Dave\Documents\CODING\projets\dvp5\main.cpp||In function `int main()'
    C:\Users\Dave\Documents\CODING\projets\dvp5\main.cpp|17|warning: exception of type `B*' will be caught|
    C:\Users\Dave\Documents\CODING\projets\dvp5\main.cpp|14|warning: by earlier handler for `A*'|
    ||=== Build finished: 0 errors, 2 warnings ===|

  4. #4
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    ne jamais lancer d'exception en allouant dynamiquement un objet car new peut lancer des exceptions ce qui peut encore mettre plus le bazard.

    Il faut allouer tes objets sur la pile et attraper les exceptions par références constante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <iostream>
     
    using namespace std;
     
    class A {};
    class B : public A{};
     
    int main()
    {
        try
        {
    	B b;
            throw b;
        }catch (const A&)
        {
            cout << "..." << endl;
          } catch (const B&)
        {
            cout << "!!!" << endl;
        }
        return 0;
    }
    Comme B dérive de A, j'ai ici un warning de g++ car le second bloc ne sera jamais utilisé à cause de l'upcasting implicite de B en A et de la linéarité de l'évaluation des blocs "catch" (dès qu'un bloc convient, on ne regarde pas les autres, même si un meilleur existe pourtant après)
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  5. #5
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Citation Envoyé par Dreambeliever Voir le message
    Ca marche avec les pointeurs!

    Le code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <iostream>
     
    using namespace std;
     
    class A {};
    class B : public A{};
     
    int main()
    {
        try
        {
            throw new B();
        }catch (A*)
        {
            cout << "..." << endl;
        } catch (B*)
        {
            cout << "!!!" << endl;
        }
        return 0;
    }
    me donne même un warning:

    Ouai enfin, vla la misére pour gérer la mémoire après, c'est bon pour se taper des segfaults.. Bref comme la dit david c'est par référence constante qu'on attrape les exceptions.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    57
    Détails du profil
    Informations personnelles :
    Âge : 16
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 57
    Points : 65
    Points
    65
    Par défaut
    Je n'ai jamais dit le contraire, je voulais bien faire remarquer que ça marche aussi avec les pointeurs. (d'ailleurs on peut utiliser des pointeurs sans faire d'allocation dynamique)

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par Dreambeliever Voir le message
    Je n'ai jamais dit le contraire, je voulais bien faire remarquer que ça marche aussi avec les pointeurs. (d'ailleurs on peut utiliser des pointeurs sans faire d'allocation dynamique)
    Ca, c'est beaucoup plus complexe à faire... et cela ne sert à rien

    En gros Cela signifie que:
    1. tu dois créer un objet du type de ton exception en dehors du bloc try
    2. tu dois récupérer l'adresse de de cet objet dans le bloc try en vue de lancer l'exception OU
    3. que tu passe l'exception par pointeur à la fonction qui risque de la lancer, ce qui implique [1] et [2] et n'apporte strictement rien
    En d'autres termes: pourquoi faire simple quand on peut faire compliqué

    (pour info, je remettrais plus facilement en cause le fait de récupérer l'exception sous une forme constante que celui de la lancer par valeur et de l'attraper par référence )

    N'oublie pas que tout est fait en C++ pour essayer de t'éviter au maximum le recours aux pointeurs et à tout ce qu'ils peuvent sous-entendre (dont, principalement, l'allocation dynamique)...

    Au final, l'utilisation même des pointeurs devrait se limiter aux cas où:
    • il est nécessaire de créer dans un objet contenu une relation avec l'objet contenant
    • il est nécessaire de créer un objet en relation avec un (ou des) objets de type identique (pile, file, liste, et tous ces genres de choses que la STL implémente de manière si efficace)
    • il est nécessaire de créer une relation de contenant à contenu pour laquelle le contenu peut ne pas exister
    • il est nécessaire de créer une relation de contenant à contenu pour laquelle la responsabilité de la durée de vie du contenu ne dépend pas du contenant
    et l'utilisation de l'allocation dynamique devrait se limiter, à peu de choses près aux cas
    • de la mise en oeuvre du polymoprhisme lorsque l'objet polymophe doit être renvoyé par la fonction
    • lorsqu'un objet ne pouvant pas être copié doit survivre à la fonction qui le crée afin d'être renvoyé par cette fonction

    En dehors de ces cas bien particuliers, l'utilisation de pointeurs et de l'allocation dynamique, surtout si ce n'est pas encadré par une politique d'utilisation de pointeurs intelligents, reviendra souvent à courir le risque de te tirer une balle dans le pied un jour ou l'autre...

    Maintenant, ne me fait pas dire ce que je n'ai pas dit:

    J'estime, à contrario de ce que pourrait laisser croire ce que j'ai écrit plus haut, qu'il est important de maitriser les phénomènes propres aux pointeurs et à l'allocation dynamique...

    Simplement, il faut avouer que, même si le programmeur est chevronné, il souffre de son humanité, qui le rend, par nature, susceptible de commettre une erreur.

    Personne n'est donc à l'abri du fait de provoquer, souvent par inattention, un phénomène de fuite de mémoire ou de double libération de la mémoire aux conséquences pouvant être catastrophiques, lorsqu'il s'agit de recourir aux pointeurs et à l'allocation dynamique de la mémoire.

    C'est pourquoi le meilleur conseil à donner est d'envisager de n'y recourir que lorsque l'on n'a vraiment pas d'autres solutions et de ne le faire en veillant à apporter un soin et une attention toute particulière à ce que l'on fait
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    57
    Détails du profil
    Informations personnelles :
    Âge : 16
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 57
    Points : 65
    Points
    65
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    Ca, c'est beaucoup plus complexe à faire... et cela ne sert à rien

    En gros Cela signifie que:
    1. tu dois créer un objet du type de ton exception en dehors du bloc try
    2. tu dois récupérer l'adresse de de cet objet dans le bloc try en vue de lancer l'exception OU
    3. que tu passe l'exception par pointeur à la fonction qui risque de la lancer, ce qui implique [1] et [2] et n'apporte strictement rien
    En d'autres termes: pourquoi faire simple quand on peut faire compliqué

    (pour info, je remettrais plus facilement en cause le fait de récupérer l'exception sous une forme constante que celui de la lancer par valeur et de l'attraper par référence )

    N'oublie pas que tout est fait en C++ pour essayer de t'éviter au maximum le recours aux pointeurs et à tout ce qu'ils peuvent sous-entendre (dont, principalement, l'allocation dynamique)...

    Au final, l'utilisation même des pointeurs devrait se limiter aux cas où:
    • il est nécessaire de créer dans un objet contenu une relation avec l'objet contenant
    • il est nécessaire de créer un objet en relation avec un (ou des) objets de type identique (pile, file, liste, et tous ces genres de choses que la STL implémente de manière si efficace)
    • il est nécessaire de créer une relation de contenant à contenu pour laquelle le contenu peut ne pas exister
    • il est nécessaire de créer une relation de contenant à contenu pour laquelle la responsabilité de la durée de vie du contenu ne dépend pas du contenant
    et l'utilisation de l'allocation dynamique devrait se limiter, à peu de choses près aux cas
    • de la mise en oeuvre du polymoprhisme lorsque l'objet polymophe doit être renvoyé par la fonction
    • lorsqu'un objet ne pouvant pas être copié doit survivre à la fonction qui le crée afin d'être renvoyé par cette fonction

    En dehors de ces cas bien particuliers, l'utilisation de pointeurs et de l'allocation dynamique, surtout si ce n'est pas encadré par une politique d'utilisation de pointeurs intelligents, reviendra souvent à courir le risque de te tirer une balle dans le pied un jour ou l'autre...

    Maintenant, ne me fait pas dire ce que je n'ai pas dit:

    J'estime, à contrario de ce que pourrait laisser croire ce que j'ai écrit plus haut, qu'il est important de maitriser les phénomènes propres aux pointeurs et à l'allocation dynamique...

    Simplement, il faut avouer que, même si le programmeur est chevronné, il souffre de son humanité, qui le rend, par nature, susceptible de commettre une erreur.

    Personne n'est donc à l'abri du fait de provoquer, souvent par inattention, un phénomène de fuite de mémoire ou de double libération de la mémoire aux conséquences pouvant être catastrophiques, lorsqu'il s'agit de recourir aux pointeurs et à l'allocation dynamique de la mémoire.

    C'est pourquoi le meilleur conseil à donner est d'envisager de n'y recourir que lorsque l'on n'a vraiment pas d'autres solutions et de ne le faire en veillant à apporter un soin et une attention toute particulière à ce que l'on fait

    Citation Envoyé par Dreambeliever
    Je n'ai jamais dit le contraire
    Je suis d'accord que le plus simple c'est les références constantes et qu'il n'y a pas de raison de préférer autre chose.

    Mais plus haut il était dit que ça ne marchait pas avec des pointeurs, ce qui est faux.

  9. #9
    Membre actif
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Points : 233
    Points
    233
    Par défaut
    En passant un objet par référence le destructeur de la class est appelée entre throw et catch et je perds les informations contenus par l'objet paramètre de catch.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    #include <stdio.h>
     
    class foo
    {
    public:
    	char *buffer;
     
    	foo(void)
    	{
    		buffer = new char[25];
    		sprintf(buffer, "Suis-je toujours là?!");
    	}
     
    	~foo(void)
    	{
    		delete[] buffer; // J'y suis plus!
    	}
    };
     
    void main(void)
    {
    	try {
    		foo f;
    		throw f;
    	} catch(foo &f) {
    		printf(f.buffer); // buffer est libéré automatiquement.
    	}
    }

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par emmr.rida Voir le message
    En passant un objet par référence le destructeur de la class est appelée entre throw et catch et je perds les informations contenus par l'objet paramètre de catch.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    #include <stdio.h>
     
    class foo
    {
    public:
    	char *buffer;
     
    	foo(void)
    	{
    		buffer = new char[25];
    		sprintf(buffer, "Suis-je toujours là?!");
    	}
     
    	~foo(void)
    	{
    		delete[] buffer; // J'y suis plus!
    	}
    };
     
    void main(void)
    {
    	try {
    		foo f;
    		throw f;
    	} catch(foo &f) {
    		printf(f.buffer); // buffer est libéré automatiquement.
    	}
    }
    Absolument pas, étant donné que, lorsque tu transmet une classe par référence constante, s'il n'existe pas d'objet, un objet temporaire est construit en utilisant le constructeur par défaut...

    En plus, l'exemple que tu donne est l'exemple type de ce qu'il ne faut pas faire, et ce pour une série de raisons
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre actif
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Points : 233
    Points
    233
    Par défaut
    Oui, c'est vrai mais les données allouées dynamiquement à partir des paramètres que je fournie lors du throw sont perdu. Dans l'exemple que j'ai fournis dans mon dernier post, le buffer allouée dans le constructeur n'est pas récupéré par l'objet paramètre de catch.

    PS : Le problème principal de cette discussion est résolu et ainsi sera la discussion. Je sais pas ce que je faisais de travaers!
    Merci pour vos réponses.

    Que la discussion continue...

  12. #12
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Un objet jeté est toujours copié. La différence entre le catch par valeur ou par référence est de savoir s'il est copié une deuxième fois.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  13. #13
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Dans ce cas, est ce qu'on pourait imaginer le cas suivant :
    1) une exeception est lancé à cause du manque de mémoire
    2) elle est copié, il n'y a vraiment plus de mémoire
    3) goto 1)

    Ou c'est du domaine de la SF de l'informatique ?
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  14. #14
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Tu peux te retrouver dans la situation où la copie n'est pas possible parce que l'exception est trop grosse (la copie se fait habituellement vers une zone réservée statiquement, certaines implémentations peuvent agrandir cette zone dynamiquement si besoin est). Je ne me souviens pas avoir jamais su ce qui devait se passer (std::terminate() me semble être un bon candidat, mais j'ai pas le temps de vérifier).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  15. #15
    Membre actif
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Points : 233
    Points
    233
    Par défaut
    Citation Envoyé par emmr.rida Voir le message
    Oui, c'est vrai mais les données allouées dynamiquement à partir des paramètres que je fournie lors du throw sont perdu. Dans l'exemple que j'ai fournis dans mon dernier post, le buffer allouée dans le constructeur n'est pas récupéré par l'objet paramètre de catch.
    La classe de l'objet jeté par throw n'implémente pas un constructeur de copie. En y ajoutant un, le throw d'objet par référence passe comme il faut.

    Encore merci pour vos réponses. Je dois maintenant modifier les throw pour jeter les objets par référence et non par allocation dynamique.

  16. #16
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    La formule classique pour la règle est "throw by value, catch by reference" (même si je préciserais "const reference").
    Si la bibliothèque MFC de Microsoft ne respecte pas cette règle, c'est qu'elle a été faite avant que la règle soit complètement standardisée.

    Quant à Java et .Net, c'est la philosophie opposée, donc ne pas s'en inspirer pour C++...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. classe dérivée de thread
    Par aaronw dans le forum Threads & Processus
    Réponses: 9
    Dernier message: 08/03/2006, 15h35
  2. Réponses: 2
    Dernier message: 06/12/2005, 09h41
  3. Réponses: 4
    Dernier message: 20/11/2005, 05h48
  4. [MFC] CArray et classe dérivée?
    Par TigreRouge dans le forum MFC
    Réponses: 14
    Dernier message: 02/08/2005, 22h45
  5. Déterminer le type d'une class dérivée
    Par LDDL dans le forum MFC
    Réponses: 3
    Dernier message: 10/12/2004, 17h36

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo