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 :

pb cast heritage


Sujet :

C++

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    167
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 167
    Par défaut pb cast heritage
    Bonjour,

    J'ai fait un code ultra simple sous code::blocks : 1 template, 1 classe héritant d'une specification du template, 1 classe héritant de cette dernière ; le tout, sans aucun pointeur.

    J'ai tourné le pb sous pas mal d'angle mais en vain.
    ça compile mais à l'affichage, pb, c'est pas ce à quoi je m'attendais : j'ai une adresse et pas un contenu! Pourtant, je suis certain de demander un contenu...
    Les questions que je me pose actuellement pour résoudre le problème :
    _est-ce que l'héritage d'un template spécifié peut causer des soucis?
    _est que le static_cast est bien suffisant pour passer d'une dérivée à sa base (comme dans la FAQ)?
    _est-ce que les opérateurs par défaut sont bien là?

    Je met en pj les sources et ci dessous, ce qui me fait l'affichage :

    Affichage.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #ifndef AFFICH_H
    #define AFFICH_H
     
    #include <fstream>
     
    #include "Bornes.h"
    #include "Spec_servo.h"
    #include "Servo.h"
     
    std::ostream& Print(std::ostream& stream, Spec_servo& b);
    std::ostream& Print(std::ostream& stream, Servo& b);
     
    #endif

    Affichage.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #include "Affichage.h"
     
    std::ostream& Print(std::ostream& stream, Spec_servo& b)
       {
        return stream<<"Spec_servo : "<<b.r_min()<<" ; "<<b.r_max();
       }
     
    std::ostream& Print(std::ostream& stream, Servo& b)
       {
        //Spec_servo *spe=static_cast<Spec_servo*>(b);
        Spec_servo spe=static_cast<Spec_servo>(b);
        return stream<<"Servo : "<<Print(stream,spe)<<b.r_Pulse();
       }
    RQ : le static_cast direct compile, pas celui à * comme dans la FAQ... j'ai donc un soucis et je suis aveugle depuis 8H!

    Merci d'avance si vous détecter qq chose de bizarre.
    Fichiers attachés Fichiers attachés

  2. #2
    screetch
    Invité(e)
    Par défaut
    nah nah nah nah nah!!

    ca va pas du tout.

    ici en fait avec ton sttaic_cast tu fais une copie de l'objet, que tu mets dans un objet de type Spec_servo

    ce que tu veux faire c'est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
        Spec_servo *spe=static_cast<Spec_servo*>(&b);
        Spec_servo &spe=static_cast<Spec_servo&>(b);
    Dans les deux cas les static_cast sont inutiles :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
        Spec_servo *spe=&b;
        Spec_servo &spe=b;

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    167
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 167
    Par défaut
    ...ok

    ...par contre, j'ai toujours une adresse sur la console...

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 634
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 634
    Par défaut
    Salut,

    Déjà, je ne partirais sans doute pas du principe que ma borne travaille d'office avec des valeurs numériques... Ni même sur le fait que ces valeurs soient d'office des entiers

    Je mettrais donc plutôt en place un système de traits de politiques pour permettre, le cas échéant, de travailler avec autre chose... Mais cela n'a rien à voir avec ton problème... et je propose donc de remettre cette discussion à plus tard

    Enfin, ce n'est jamais qu'un avis strictement personnel, mais je préfères généralement le comportement de l'action de renvoi de valeur.

    ainsi, je préfères généralement écrire un code du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::ostream& Print(std::ostream& stream, Spec_servo& b)
    {
         stream<<"Spec_servo : "<<b.r_min()<<" ; "<<b.r_max();
         return stream;
    }
    (même si je t'accorde que cela revient exactement au même, je trouve le code plus clair )

    Mais bon, ce n'est, ici encore, qu'un avis strictement personnel, qui n'a rien à voir avec le problème actuel

    Mais venons en à ton problème.

    Tu perd de vue un principe primordial pour ce qui concerne l'héritage: une classe dérivé est du type de la classe mère.

    Ainsi, dans ton code, un objet du type Servo EST un objet du type Spec_servo, qui EST lui même un objet de type Bornes<int>.

    Cela implique que, si tu as un objet de type Servo, et que tu veux le faire passer en argument à une fonction qui attend un Spec_servo, tu n'a absolument rien à faire pour le transtyper: cela se fait implicitement

    L'inconvénient devient que, du coup, le fait d'avoir deux fonctions libres (AKA en dehors de toute classe) de même noms, mais prenant en argument un référence sur un objet de type Servo pour l'une et une référence de type Spec_servo pour l'autre peuvent facilement entrer en conflit s'il s'agit de passer un objet de type Servo.

    L'avantage dont tu dispose, c'est que le comportement de la fonction qui reçoit un objet de type Servo ne fait que rajouter un comportement "périphérique" au comportement de la fonction qui reçoit un Spec_servo, et que, en transformant ces deux fonctions en méthodes, il deviendra facile de profiter du polymorphisme, et même d'appeler la méthode de la classe de base dans la méthode de la classe dérivée

    Dés lors, il pourrait sembler intéressant:
    1. De transformer tes fonctions print en méthodes virtuelles, afin de pouvoir profiter au mieux du polymorphisme
    2. de "simplement" signaler à la méthode print qui s'applique au Servo que tu veux qu'elle appelle la méthode du même nom s'appliquant à un objet de type Spec_servo
    3. de définir l'opérateur << pour ton o(f)stream de manière à ce qu'il appelle la méthode polymorphique (ou n'importe quelle autre méthode de ton choix, non polymorphique et définie uniquement dans la classe de base)
    4. de veiller à la "const-correctness" pour tout ce qui touche aux objets de type Spec_servo et dérivées


    Au final, tu aurais dans ta classe Spec_servo quelque chose ressemblant à:
    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
    class Spec_servo :public Bornes<int>
    {
        public:
        /* je passe sur les autres méthodes ;) */
            friend 
            std::ofstream& operator << (std::ofstream&, const Spec_servo&);
        protected:
            virtual void Print(std:ofstream&) const
    };
    void Spec_servo::Print(std::ofstream& stream) const
    {
        stream<<"Spec_servo : "<<r_min()<<" ; "<<r_max();
    }
    std::ostream& operator << (std::ostream& stream,const Spec_servo& b)
    {
        b.Print(stream);
        return stream;
    }
    et, au niveau de ta classe Servo, tout simplement quelque chose ressemblant à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Servo: public Spec_servo
    {
        /* je passe sur tout le reste ;) */
        protected:
            virtual void  Print(std::ofstream&) const;
    };
    void Servo::Print(std::ofstream& stream) const
    {
        stream<<"Servo :";
        Spec_servo::Print(stream);
        stream<<PULSE;
    }
    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

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    167
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 167
    Par défaut
    Merci pour les remarques, je mets ça de côté pour le moment, je fais une petite application embarquée donc, je vais faire dans le simple... et déjà ça coince!

    C'est effectivement ce que je voulais faire dans un premier temps la surchage du << ... et j'ai eu qq soucis. ... et en fait, ils sont toujours là!
    Premièrement, j'ai rajouté des destructeurs virtual comme me le spécifiait le compilateur... pas trop dur : virtual ~CLASS_NOM(){}
    Et ensuite... ben, je croyais que ça venait d'un problème de const... mais je crois pas. J'ai l'erreur de compilation suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    In function «std::ostream& operator<<(std::ostream&, const Spec_servo&)":
    erreur: no matching function for call to «Spec_servo::Print(std::basic_ostream<char, std::char_traits<char> >&) const"
    note: candidats sont: virtual void Spec_servo::Print(std::ofstream&) const
    ...ben, c'est pourtant ce que je lui donne, non?!

    RQ : <iostream> est-il nécessaire dans la partie définition de la classe (parce qu'avec ou sans, ça le gène pas!)
    RQ2 : instruciton "Print" dans le cpp de la classe correspondante et le "operator<<" dans le fichier Affichage.cpp (dédié)

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 392
    Par défaut
    La fonction Print() demande spécifiquement un ofstream (on se demande pourquoi) alors que tu lui passes simplement un ostream.
    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.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 634
    Par défaut
    en fait, c'est ma faute... au moment où j'ai répondu au message, il me semblait qu'il utilisait des ofstream (il faut vraiment que j'arrête de répondre passé une certaine heure )

    remplace donc tous les std::ofstream& par des std::ostream, et tout ira pour le mieux

    Pour répondre à tes autres questions:

    L'inclusion du fichier d'en-tête <iostream> doit s'effectuer avant la première utilisation d'une des classes qu'il fournit, sauf si tu utilise un pointeur sur cette classe, au quel cas, une déclaration anticipée peut suffire, du moins, tant que tu ne fais apple ni à une instance de la classe ni à un membre ou une méthode de la classe déclarée anticipativement.

    Dans ton exemple, l'inclusion du fichier <iostream> peut donc s'effectuer dans le seul fichier Spec_servo.h

    Tu peux implémenter une fonction dans n'importe quel fichier, pour autant que tu respecte deux règles:

    1. Le "one definition rule" qui t'oblige à n'implémenter qu'une fois chaque fonction (autrement dit, que tu n'implémente pas une deuxième fois ta fonction dans un autre fichier)
    2. Que le fichier d'implémentation inclue (au besoin de manière indirecte, par le jeu des inclusions en cascade) l'ensemble des éléments dont elle a besoin
    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 confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    167
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 167
    Par défaut
    le ofstream, j'avais vu mais merci pour les suivants, j'avais oublié de le notifer.

    ok pour le header

    Et en fait pour "caster" simplement mes objets, je fais (MERE)obj_FILLE... c'est plus simple et efficace!

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 392
    Par défaut
    Sauf que pour les classes mères, on n'a normalement pas besoin de cast explicite (si c'est un pointeur ou une référence, du moins), et encore moins d'un cast C-Style. Si tu utilises un cast, utilise un static_cast<>.
    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.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 634
    Par défaut
    Le transtypage (cast) est parfaitement inutile quand tu veux utiliser un objet du type d'une classe dérivée comme s'il s'agissait d'un objet du type de sa(d'une de ses) classe(s) de base, pour la simple et bonne raison qu'il est tout à la fois objet du type de la classe dérivée et objet du type de la classe de base.

    C'est la raison pour laquelle le compilateur se plaint d'une ambiguïté s'il rencontre deux fonctions libres (AKA qui ne font partie d'aucune classe) de même nom et dont la seule différence réside dans le fait que l'une demande un objet du type de la classe fille et l'autre un objet du type de la classe mère, comme par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    void fonction(const fille& f);
    void fonction(const mere& m);
    lors d'un appel en fournissant un objet du type de la classe fille.

    Le transtypage n'est nécessaire (utile ) que si tu dispose d'un conteneur de pointeurs sur objets du type de la classe de base et que tu veux spécifiquement récupérer un objet du type de la classe dérivée, mais il faut alors garder en mémoire le fait que tu risque de voir au sein de ce conteneur des objets qui sont ne réellement que du type de la classe de base, et d'autre qui sont en réalité effectivement des objets du type d'une des classes dérivées.

    C'est à dire que, si tu as créé une arborescence d'héritage dont la classe de base est "mammifère" et dont les classes dérivées sont "chat", "chien" et "lapin", tu trouveras entre autre dans le conteneur des chats (dont le transtypage en chien ou en lapin posera problème), des chiens (dont le transtypage en chat ou en lapin posera problème) et des lapins (dont le transtypage en chat ou en chien posera problème).

    Mais tu pourrais tout aussi bien trouver également des "mammifères" pour lesquels il n'existe pas de classe dérivée adaptée: lion, éléphant, girafe, renard, loup... et dont une tentative de transtypage en chat, en chien ou en lapin posera problème.

    Tu dois donc t'assurer de ce que le transtypage vers une classe dérivée est possible, ce qui disqualifie d'office le transtypage C-style.

    Selon ne niveau de certitude que tu as que l'objet est bel et bien un chat, un chien ou un lapin, et ta politique de gestion d'erreur, tu devra passer par les transtypages C++ que sont static_cast et dynamic_cast (qui ont en plus l'énorme avantage par rapport au transtypage C-style de faciliter une recherche éventuelle dans le code )
    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

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

Discussions similaires

  1. Debutant, cast, heritage
    Par Christophe78180 dans le forum Débuter
    Réponses: 2
    Dernier message: 05/04/2011, 19h40
  2. Heritage et cast
    Par LordBob dans le forum C++
    Réponses: 7
    Dernier message: 23/04/2007, 08h37
  3. [heritage] cast
    Par r0d dans le forum C++
    Réponses: 2
    Dernier message: 24/04/2006, 15h36
  4. cast et heritage
    Par BigNic dans le forum C++
    Réponses: 11
    Dernier message: 30/03/2006, 08h07

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