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 :

Probleme de forward declaration


Sujet :

C++

  1. #1
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2005
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 162
    Par défaut Probleme de forward declaration
    Voila, pour eviter un include, je veux faire une forward declaration.
    Le probleme c'est que ca concerne une nested class :

    class A;
    class A::B; // compil error : expected type-name

    void myFunction(const A::B&);

    Comment faire pour bien ecrire une predeclaration d'une classe imbriquee ?

  2. #2
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut
    Tu ne peux pas. A partir du moment où tu essayes d'accéder à un champ/type/fonction d'une classe A, il faut que celle-ci ait été définie. Donc nécessité d'un #include.

  3. #3
    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,

    Par contre, rien ne t'empêche normalement de faire une déclaration anticipée de ta classe imbriquée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class A;
    class A::B;
     
    void MaFonction(/*const*/ A::B&);
    (attention: la déclaration imbriquée de B doit être effectuée dans la partie publique de A )
    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

  4. #4
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2005
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 162
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    Par contre, rien ne t'empêche normalement de faire une déclaration anticipée de ta classe imbriquée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class A;
    class A::B;
     
    void MaFonction(/*const*/ A::B&);
    (attention: la déclaration imbriquée de B doit être effectuée dans la partie publique de A )
    Ben, c'est exactement ce que j'ai fait.
    Et B est bien publique dans A

  5. #5
    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
    Je viens de vérifier... la déclaration anticipée ne fonctionne effectivement pas dans le cadre d'un classe imbriquée. Au temps pour moi

    Ceci dit, tu n'évitera pas l'include à un moment où un autre, même dans le cas où une déclaration anticipée apparait

    Dés le moment où tu veux utiliser une structure définie, il n'y a rien à faire, il faut que l'unité de compilation dispose de... la définition réelle de cette structure:
    • pour en connaître les noms des différents membres
    • pour en connaître les noms des différentes méthodes
    • pour en connaître la taille

    Tu ne peux d'ailleurs envisager sereinement la déclaration anticipée que si tu n'a pas besoin du type complet avant qu'il n'apparaisse.

    Ainsi, un code du genre 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
    #ifndef A_H
    #define A_H
    class B;
     
    class A
    {
        public:
            A(B&);
            virtual ~A();
            const B GetAsB();
        protected:
        private:
            int v;
    };
    #endif // A_H
    fonctionnera avec un fichier d'implémentation sous la forme 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
     
    #include "A.h"
    #include "B.h"
     
    A::A(B& b):v(b.GetVal())
    {
     
    }
    A::~A()
    {
     
    }
    const B A::GetAsB()
    {
        return B(v);
    }
    (sous réserve, évidemment, de l'existence du fichier B.h) alors qu'une classe du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class A
    {
        public:
            explicit A(const B&);
            ~A();
        private:
            B b; 
    };
    nécessitera de connaître la définition complète de B pour être en mesure de créer la classe A.

    Il s'en suit qu'il faudra soit inclure B.h avant de définir la classe A, soit définir les deux classes dans le même fichier, mais en définissant B avant A

    [EDIT]Par contre, la taille d'un pointeur étant d'office connue, rien ne t'empêcherait de prévoir une classe sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class B;
    class A
    {
        public:
            A(B*);
            ~B();
        private:
            B* b;
    };
    et de ne prévoir la définition complète de la classe B qu'après la classe A (ou via inclusion de B.h dans le fichier d'implémentation)

    Mais, quoi qu'il en soit, il semble raisonnable d'envisager le fait que tu auras de toutes façons besoin de connaître l'intégralité de la classe B pour implémenter les différentes méthodes de la classe A
    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

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Par défaut
    Faudrait déja savoir pour quelle raison tu veux déclarer ta fonction avant de définir ta classe, je n'en vois du tout l'utilité.
    D'après mon expérience, je dirais que les déclarations anticipées ne sont utiles que dans le cas d'interdépendance entre des classes (notamment avec les classes amies), et aussi qu'il vaut vraiment mieux les déclarer dans le même header (j'ai déja passé des heures à essayer de trouver un barème quelconque pour les dépendances inter-fichiers, j'ai fini par laisser tomber).

  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
    Citation Envoyé par zais_ethael Voir le message
    Faudrait déja savoir pour quelle raison tu veux déclarer ta fonction avant de définir ta classe, je n'en vois du tout l'utilité.
    D'après mon expérience, je dirais que les déclarations anticipées ne sont utiles que dans le cas d'interdépendance entre des classes (notamment avec les classes amies), et aussi qu'il vaut vraiment mieux les déclarer dans le même header (j'ai déja passé des heures à essayer de trouver un barème quelconque pour les dépendances inter-fichiers, j'ai fini par laisser tomber).
    Non...

    La déclaration anticipée peut, tout simplement, servir à éviter d'exposer toutes les dépendances dans le fichiers d'en-tête...

    Avec QT, par exemple, il est "d'usage" de travailler 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
    #ifdef ...
    #define ...
     
    class Dependance1;
    class Dependance2;
    /*...*/
    class MaClasse
    {
         /*...*/
    };
    et de ne placer les fichiers inclus que dans le fichier d'implémentation.

    Bon ou mauvais, cela permet malgré tout de ne pas forcer la dépendance sur le fichier d'en-tête
    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 émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Par défaut
    Mais ça va faire un méchant foutoir dans les inclusions de fichier, avec par exemple l'obligation d'inclure le fichier contenant la déclaration avant celui contenant la définition (si on veut utiliser les deux dans la même unité de programmation). Et normalement, ça ne marchera pas non plus si on a deux fichiers à inclure contenant chacun leur déclaration.

  9. #9
    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
    J'ai l'impression que tu ne comprend pas bien les différents thermes...

    Une délcaration anticipée, permet juste de dire qu'une classe existe, et se trouve généralement dans un fichier d'en-tête sous une forme proche de
    La définition d'une classe indique le contenu de cette classe, se trouve dans le fichier d'en-tête et fournit la déclaration des membres et méthodes sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class LaClass
    {
        public:
            LaClass(); /* déclaration du constructeur */
            ~LaClass(); /* déclaration du destructeur */
        private:
            int m; /*déclaration du membre nommé m */
    };
    L'implémentation de la classe consiste à fournir la définition /l'implémentation des méthodes de cette classe et ne doit normalement pas se trouver dans le fichier d'en-tête (sauf cas particulier), mais dans un fichier "d'implémentation" séparé (typiquement, un fichier avec l'extension *.cpp)

    Un fichier portant l'extension *.cpp ne doit jamais être inclus dans un fichier d'en-tête, mais doit, au minimum, inclure les fichiers dans lesquels sont définies les classes dont il doit définir les méthodes.

    Si un fichier d'implémentation doit se trouver dans un fichier d'en-tête, comme par exemple en ce qui concerne les classes template, il est très largement conseillé d'éviter l'extension *.cpp pour éviter, justement, toute confusion.

    Enfin, pour pouvoir instancier en mémoire un objet du type d'une classe donnée, et, à moins de se contenter d'un pointeur au départ duquel on n'essayerait pas d'accéder à un membre ou à une méthode, il faut disposer de la définition complète de la classe en question (mais pas de l'implémentation de ses méthodes )
    Ainsi, tu pourrait très bien envisager un fichier "A.h" contenant
    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
     
    #ifndef A_H
    #define A_H
    class B;
     
    class A
    {
        public:
            A(B*);
            virtual ~A();
            B* GetB();
        protected:
        private:
            B* v;
    };
    #endif // A_H
    qui, comme il ne fait qu'utiliser un pointeur de type B n'a pas besoin, en l'état, d'autre chose que le fait de savoir que la classe B existe.

    Le fichier d'implémentation prendrait la forme 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
    #include "A.h"
     
    A::A(B* b):v(b)
    {
     
    }
    A::~A()
    {
     
    }
    const B* A::GetB()
    {
        return v;
    }
    Tu pourrait envisager une classe B héritant de A et dont le fichier d'en-tête prendrait la forme 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
    #ifndef B_H
    #define B_H
    #include "A.h" /* le fait qu'il y ait héritage fait qu'il faut disposer de la
                        * définition complete
                        */
    class B:public A
    {
        public:
            B(B* b, int val);
            ~B();
            int Value();
        private:
            int val;
    };
    #endif // B_H
    et dont le fichier d'implémentation prendrait la forme 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
    #include "B.h"
    B::B(B* b, int val):A(b),val(val)
    {
        //ctor
    }
     
    B::~B()
    {
        //dtor
    }
    int B::Value()
    {
        return val;
    }
    Là où les choses se compliqueraient, c'est dans le cas où tu voudrait ajouter une méthode telle que
    directement dans la classe A, en prévoyant de la définir sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    B& A::Reference()
    {
        return *v;
    };
    En effet, comme cette fonction ne renvoie pas un pointeur mais bel et bien une instance de l'objet de type B, la fonction (et donc le fichier dans lequel elle est définie) doit... disposer de la définition complete du type B

    On entre de plein pied dans le problème d'inclusions cycliques( A qui inclut B et B qui inclut A).

    On ne peut donc pas mettre l'inclusion de B.h dans A.h, et, pour que A.cpp puisse disposer de la définition complète de B, on doit donc effectuer l'inclusion... directement dans A.cpp
    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

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Par défaut
    Wah, merci pour ce cours sur le C++. Oui, je sais très bien ce que sont les déclarations/définitions/implémentation.
    Je pensais que plusieurs déclarations/définitions de classes imbriquées dans n'importe quel ordre poseraient problème, mais après avoir testé deux secondes je viens de me rendre compte que ce n'était pas le cas . Donc fais comme si j'avais rien dit.

    Sinon:
    Citation Envoyé par koala01 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    B& A::Reference()
    {
        return *v;
    };
    En effet, comme cette fonction ne renvoie pas un pointeur mais bel et bien une instance de l'objet de type B, la fonction (et donc le fichier dans lequel elle est définie) doit... disposer de la définition complete du type B
    Ou vois tu une instance toi? Je ne vois qu'une réfèrence. Teste, et tu verras que déclarer cette fonction fonctionne très bien avec une simple déclaration de B.

  11. #11
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut
    Citation Envoyé par zais_ethael Voir le message
    Ou vois tu une instance toi?
    Ici :


    Citation Envoyé par zais_ethael Voir le message
    ]Je ne vois qu'une réfèrence. Teste, et tu verras que déclarer cette fonction fonctionne très bien avec une simple déclaration de B.
    La déclaration de la fonction oui ; la définition non.

Discussions similaires

  1. Probleme de forward declaration
    Par crazycrow dans le forum Langage
    Réponses: 4
    Dernier message: 28/05/2009, 10h21
  2. Probleme de forward declaration
    Par vandamme dans le forum Bibliothèques
    Réponses: 0
    Dernier message: 05/10/2007, 18h16
  3. Probleme de forward declaration
    Par porco dans le forum C++
    Réponses: 2
    Dernier message: 15/10/2006, 17h15
  4. [struts]probleme de forward
    Par VincentP dans le forum Struts 1
    Réponses: 4
    Dernier message: 15/09/2005, 14h42
  5. Réponses: 5
    Dernier message: 10/11/2004, 17h23

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