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 :

forwarder une declaration


Sujet :

C++

  1. #1
    Membre du Club
    Inscrit en
    Janvier 2008
    Messages
    74
    Détails du profil
    Informations forums :
    Inscription : Janvier 2008
    Messages : 74
    Points : 56
    Points
    56
    Par défaut forwarder une declaration
    bonjour,

    pourquoi on forward la declaration d'une class

    Merci

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

    Informations professionnelles :
    Activité : aucun

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

    Le compilateur lit le code source un peu à la manière dont tu lis ton roman préféré: de haut en bas.

    Lorsqu'il arrive à la ligne X, il "connait" donc tous les symboles qui ont été déclarés jusqu'à la ligne X-1, mais il n'a aucune connaissance de ce qui se trouvent... aux lignes suivantes.

    Or, on ne peut utiliser que ce que le compilateur connait déjà, sous peine d'avoir un message d'erreur proche de "XX ne représente pas un type".

    En effet, lorsque tu souhaite instancier un objet, tu dois veiller à ce que le compilateur ... connaisse le type de l'objet:

    Non seulement, le compilateur doit savoir qu'il existe un type nommé NomType, par exemple, mais, pour pouvoir instancier un objet, il doit savoir... la taille que cet objet va utiliser en mémoire.

    Evidemment, si tu veux invoquer une fonction membre depuis cet objet, il faudra que le compilateur... sache que cette fonction membre existe...

    Le problème vient lorsque l'on doit créer deux types particuliers qui... font référence l'un à l'autre...

    En effet, si tu as une classe (ou une structure) A et une classe (ou une structure) B, que A utilise B et que B utilise A, il y aura, fatalement, une classe qui sera... définie avant l'autre...

    Par chance, lorsque le compilateur n'a aucun besoin de connaitre l'espace mémoire utilisé par un type donné ni les membres et fonctions membres dont il est composé, il peut se contenter de savoir "qu'il existe" un type portant un nom donné.

    Il y a deux cas dans lesquels le compilateur peut se contenter de connaitre l'existence d'un type donné:

    Lorsque l'on utilise un pointeur vers un objet du type indiqué, parce qu'un pointeur n'est jamais qu'une adresse mémoire à laquelle on trouvera l'objet réellement concerné et qu'une adresse mémoire a une taille fixe.

    Lorsque l'on utilise une référence vers un objet du type indiqué, parce que, lorsque l'on s'intéresse au code binaire (ou assembleur) généré par l'utilisation d'une référence, on se rend compte qu'il est strictement identique... à celui utilisé par les pointeurs.

    Cependant, le seul fait de savoir qu'il existe des types nommés, pour reprendre l'exemple ci-dessus, A et B, ne sera suffisant que tant... qu'on essayera pas d'accéder au contenu d'un objet de type A ou B.

    En effet, si on souhaite accéder à un membre ou à une fonction membre du type A, le compilateur devra... savoir que le type ou la fonction en question existe, et donc, disposer de la définition complète de la classe.

    Par contre, s'il s'agit, simplement, de déclarer, dans A, un membre qui soit un pointeur ou une référence vers un objet de type B ou une fonction membre prenant en argument un pointeur ou une référence vers un objet de type A, le seul fait que le compilateur sache qu'il existe "un type nommé A (ou B)" sera largement suffisant.

    La manière de s'y prendre pour indiquer simplement au compilateur"qu'il existe un type nommé A (ou B)" s'appelle... la déclaration anticipée (forward declaration).

    Ainsi, nous pouvons envisager d'écrire les fichiers d'en-tête suivants:
    A.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
    15
    16
    17
    /* directives permettant d'éviter les inclusions multiples */
    #ifndef A_H
    #define A_H
    class B; //déclaration anticipée de B
    class A
    {
        public:
            A();
            ~A();
            void foo(B &); // on n'utilise pas vraiment B ici: on indique "juste"
                           // que foo a besoin d'une référence vers un B
            void doSomething();
        private:
            B * my_b; // la taille d'un pointeur est connue...
                      // typiquement, elle est de 32 ou 64 bits sur PC
    };
    #endif  // fin de la directive contre les inclusions multiples
    B.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
    /* directives permettant d'éviter les inclusions multiples */
    #ifndef B_H
    #define B_H
    class A; //déclaration anticipée de A
    class B
    {
        public:
            void foo(A &); // on n'utilise pas vraiment A ici: on indique "juste"
                           // que foo a besoin d'une référence vers un A
        private:
            A * my_a; // le compilateur se contente de savoir qu'il existe
                      // qu'il existe un type nommé A
    };
    #endif  // fin de la directive contre les inclusions multiples
    Par contre, pour pouvoir manipuler le type A au départ du type B (ou inversément), nous devons veiller à ce que le compilateur... sache de quoi sont composé aussi bien les A que les B.

    Il faudra donc veiller à inclure les deux fichiers dans tête dans les fichiers d'implémentation:
    A.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
    15
    16
    17
    18
     
    #include <A.h> // pour disposer de la définition de A
    #include <B.h> // pour disposer de la définition de B
    void A::foo(B& ref)
    {
        ref.foo(*this); //c'est ici que l'on a besoin de la définition de B
    }
    A::A():my_b(new B) // ou ici...
    {
    }
    A::~A()
    {
        delete my_b;
    }
    void A::doSomething()
    {
        /*... n'utilise pas forcement my_b, mais peut le faire ;) */
    }
    B.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    #include <B.h> // pour disposer de la définition de B
    #include <A.h> // pour disposer de la définition de A
    void B::foo(A& ref)
    {
        ref.doSomething; //c'est ici que l'on a besoin de la définition de A
    }
    Pour conclure, nous pouvons épingler le fait que, en plus de permettre de résoudre les problèmes liés à ces "dépendances circulaires", les déclarations anticipées permettent de respecter plus facilement la loi demeter.

    Cette loi indique en effet que si une classe A utilise une classe B et que B utilise une classe C, la classe A ne devrait avoir aucune connaissance de la classe C (ou du moins, ne devrait pas pouvoir manipuler un objet de type C).

    Si tu fait une déclaration anticipée de C dans B.h, et que tu inclues B.h (sans inclure C.h) dans A.h (ou dans A.cpp), toute tentative d'accéder à un des membres de C au départ d'une fonction membre de A se soldera... par une erreur de compilation
    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

  3. #3
    Membre du Club
    Inscrit en
    Janvier 2008
    Messages
    74
    Détails du profil
    Informations forums :
    Inscription : Janvier 2008
    Messages : 74
    Points : 56
    Points
    56
    Par défaut
    merci beaucoup koala01.

    c'est la meilleur réponse que j'ai eu depuis que je me suis inscris au forum .

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

Discussions similaires

  1. transformer une declaration autrement
    Par hb2012 dans le forum Débuter
    Réponses: 5
    Dernier message: 30/05/2012, 12h30
  2. Erreur sur une declaration de class avec un extends
    Par kkt8 dans le forum SWT/JFace
    Réponses: 2
    Dernier message: 18/11/2010, 17h40
  3. Probleme de traduction d'une declaration Delphi
    Par blondelle dans le forum C++Builder
    Réponses: 7
    Dernier message: 26/10/2008, 21h43
  4. forwarder une erreur d'un servlet vers une error.jsp
    Par jakouz dans le forum Servlets/JSP
    Réponses: 1
    Dernier message: 14/12/2006, 17h09
  5. Réponses: 1
    Dernier message: 23/08/2006, 13h01

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