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

Qt Discussion :

Récupérer une instance de la classe dérivée


Sujet :

Qt

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2014
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2014
    Messages : 6
    Points : 7
    Points
    7
    Par défaut Récupérer une instance de la classe dérivée
    Bonjour,

    j'ai un programme comportant 3 classes, une classe mère (class A) et deux classes filles (class B et class C). je souhaiterai, via la class mère, récupérer l'instance de la classe qui a été dérivée.

    Voici les .h des classes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class B;
    class C;
     
    class A
    {
    public :
    B* getB();
    C* getC();
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class B : public A
    {
    ...
    }
    J'utilise pour cela la méthode
    dynamic_cast<>()
    , mais je reçois une erreur et j'ai du mal a comprendre ce qu'elle signifie...

    Voici le code des méthodes qui posent le problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    B* A::getB()
    {
        return dynamic_cast<B*>(this);
    }
     
    C* A::getC()
    {
        return dynamic_cast<C*>(this);
    }
    Enfin, voici l'erreur qui apparaît lors de la compilation :
    erreur: cannot dynamic_cast ‘this’ (of type ‘class A* const’) to type ‘struct B*’ (target is not pointer or reference to complete type)
    Voilà, si quelqu'un à une idée pour savoir comment faire, je suis preneur!
    Merci d'avance (:

  2. #2
    Membre émérite
    Avatar de ymoreau
    Homme Profil pro
    Ingénieur étude et développement
    Inscrit en
    Septembre 2005
    Messages
    1 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur étude et développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 154
    Points : 2 834
    Points
    2 834
    Par défaut
    Ce type d'erreur se produit quand tu essayes d'utiliser un type/une classe déclaré mais pas défini. En l'occurrence tu fais class B; ce qui est une déclaration, mais quand tu codes ton dynamic_cast il a besoin de la définition complète de B, donc tu dois inclure l'en tête de la classe B dans A.cpp pour avoir cette définition.

    Cependant ton approche va un peu à l'encontre du principe de l'héritage car cela oblige A à connaitre ses classes dérivées, ce n'est pas générique comme code. J'imagine que c'est un cas particulier ici avec seulement 2 classes dérivées et qui n'est pas amené à évoluer, mais sur le principe tu te mets des futures limitations en faisant ce choix d'architecture. Il faut en être conscient et voir s'il n'y a pas une meilleure façon de faire ce que tu veux.

  3. #3
    Membre éclairé Avatar de PadawanDuDelphi
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2006
    Messages
    678
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Bâtiment

    Informations forums :
    Inscription : Août 2006
    Messages : 678
    Points : 717
    Points
    717
    Par défaut
    Hello,

    Je comprends pas trop l'intérêt de ce que tu fais.
    En théorie, l'héritage sert plutôt à manipuler des objets de type B ou C via l'interface A (ou à étendre une classe).
    Tu vas plutôt utiliser le dynamic_cast dans les classes qui utiliseront tes objets B et C.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    A* varAB = new B;
    A* varAC = new C;
     
    B* varB = dynamic_Cast<B*>(varAB);
    if (B)
     ...
    For crying out loud !

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 615
    Points : 30 631
    Points
    30 631
    Par défaut
    Salut,

    De toutes façons, vouloir connaitre les types dérivés quand on est au niveau de la classe de base, c'est toujours une très mauvaise idée, car il est normal que les classes dérivées connaissent la classe de base, mais l'inverse fini toujours par poser des problèmes, ne serait-ce que parce que le nombre de classes dérivées risque d'évoluer très rapidement, en fonction des besoins du projet.

    Ensuite, si tu as "été assez bête" pour perdre le type réel (dynamique) de ton objet, a solution pour contourner ce problème passe par deux mots clés : polymorphisme et double dispatch.

    Je veux dire par là que, si tu as le malheur de ne connaitre un pointeur que comme étant du type de la classe de base, la première solution consiste à la simple utilisation du polymorphisme. Veille cependant à respecter le LSP et à éviter d'exposer dans l'interface de la classe de base des fonctions qui n'auront du sens que dans une classe dérivée bien particulière

    Ensuite, il faut savoir que, même si on accède à un objet du type d'une classe dérivée au travers d'un pointeur (ou d'une référence) vers un objet du type de la classe de base, l'objet sait toujours quel est son type réel.

    Si le polymorphisme ne suffit pas, il reste toujours la solution de faire appel à une fonction polymorphe de l'objet qui, elle, fera appel à une fonction prenant une référence vers le type réel de l'objet, sous une forme proche de
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    class Base{
    public:
        virtual void doSomething();
    };
    class Derivee1{
    public:
        void doSomething() override;
    }
     
    class Derivee2{
    public:
        void doSomething() override;
    }
     
    void bar(Derivee1 /*const*/ &){
        //NOTA : toute l'interface PUBLIQUE de Derivee1 est disponible ic
        std::cout<<"appel depuis Derivee1\n";
    }
     
    void bar(Derivee2 /*const*/ &){
        //NOTA : toute l'interface PUBLIQUE de Derivee2 est disponible ic
        std::cout<<"appel depuis Derivee1\n";
    }
    void Derivee1::doSomething(){
        bar(*this); // appelle bar(Derivee1 /*const*/ &)
    }
     
    void Derivee2::doSomething(){
        bar(*this); // appelle bar(Derivee2 /*const*/ &)
    }
    int main(){
        std::vector<std::unique_ptr<Base>> tab;
        tab.emplace_back(std::make_unique<Derivee1>());
        tab.emplace_back(std::make_unique<Derivee2>());
        tab[0]->doSomething(); // affiche "appel depuis Derivee1"
        tab[1]->doSomething(); // affiche "appel depuis Derivee2"
        return 0;
    }
    Il existe d'ailleurs un patron de conception qui met ce principe en oeuvre. Tu le connais peut-être: c'est le patron de conception visiteur
    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
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2014
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2014
    Messages : 6
    Points : 7
    Points
    7
    Par défaut
    Bonjour,

    tout d'abord merci d'avoir répondu aussi rapidement. Alors oui en effet, cette façon de faire est un peu bizarre. J'avais volontairement pas inclus les fichiers d'entêtes pour éviter les inclusions multiples.

    En fin de compte, j'ai changé de principe en enlevant ces méthodes, et en rajoutant un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    enum type { typeB, typeC }
    dans ma classe mère pour connaître le type.

    Je cast par la suite mon objet de type A pour pouvoir accéder aux méthodes propres a chaque classe dérivée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    A* obj;
     
    B* item = (B*) obj;
    item->method();
    Merci pour le code que tu passé koala01, je prends quand même le temps de le comprendre

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 615
    Points : 30 631
    Points
    30 631
    Par défaut
    Citation Envoyé par Crashz83 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    A* obj;
     
    B* item = (B*) obj;
    item->method();
    Ce code est l'exemple même de ce qu'il ne faut faire EN AUCUN CAS!!!!

    D'abord, les cast "C style" (comme celui que ton code présente) sont très difficile à repérer dans le code, simplement parce qu'il peuvent être confondus avec la liste d'arguments d'une fonction (enfin, une liste qui ne contient qu'un argument de type pointeur )

    Ensuite, parce qu'il n'y a aucune vérification de la cohérence du transtypage : tu peux transtyper une maison en canard si tu le veux, le compilateur n'y trouvera absolument rien à y redire... Sauf que cela n'aura absolument aucun sens (et que cela occasionnera de sérieux soucis le jour où tu feras ce type de choses)

    Dés lors, si tu dois transtyper des objets en C++, tu es au minimum tenu d'utiliser les bons outils, à savoir :
    • dynamic_cast, qui vérifie à l'exécution que le transtypage souhaité est ne serait-ce que "possible"
    • static_cast, qui s'assure à la compilation que le transtypage est possible, mais qui posera problème si tu essaye de transtyper ton (pointeur sur) objet connu comme étant du type de base en un (pointeur sur) objet du type dérivé
    • const_cast, qui permet, à titre exceptionnel, de supprimer l'invariance de constance imposé sur une donné
    • reinterpret_cast qui permet d'effectuer un transtypage "barbare" (comprend : sans effectuer aucune vérification) correspondant au transtypage "C style"

    Mais, quoi qu'il en soit, l'idée même de devoir recourir à un transtypage quel qu'il soit est très souvent le symptome d'un problème de conception, et le signe flagrant qu'il y a très certainement intérêt à trouver un autre approche pour résoudre le problème auquel tu es confronté
    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

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2014
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2014
    Messages : 6
    Points : 7
    Points
    7
    Par défaut
    Je travaille sur un système assez vieux, alors je me suis adapté à cette conception... Cependant, après mon dernier post j'ai rajouter en effet un :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    B* item = dynamic_cast<B*>( obj );
    if( item != NULL)
    {
       ....
    }
    pour garder une certaine sécurité.

    Mais oui, il y a tout de même un problème de conception, je travaille actuellement dessus

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

Discussions similaires

  1. Récupérer une instance précise d'une classe
    Par Invité dans le forum C++
    Réponses: 5
    Dernier message: 09/11/2011, 07h21
  2. Réponses: 10
    Dernier message: 21/08/2009, 10h44
  3. [POO] Récupérer une instance de classe
    Par oranocha dans le forum Langage
    Réponses: 9
    Dernier message: 01/09/2008, 08h39
  4. Réponses: 4
    Dernier message: 27/07/2007, 20h34
  5. Comment récupérer une instance de Graphics::TGraphic ?
    Par Invité dans le forum C++Builder
    Réponses: 2
    Dernier message: 11/12/2006, 15h01

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