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 :

Déclaration/Définition de classe dans un header ET One Definition Rule


Sujet :

C++

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 4
    Par défaut Déclaration/Définition de classe dans un header ET One Definition Rule
    Bonjour,

    Je voudrais avoir votre avis pour savoir ce que l'on a le droit de mettre dans un header file...

    D'après la norme C++, au §3.2 "One definition rule", il est dit
    "No translation unit shall contain more than one definition of any variable, function, class type, enumeration type or template"

    Dans un header, il faut donc éviter les définitions sinon en cas d'inclusion multiple ou récursive de header, ces définitions seront répétées dans la translation unit (ce qui implique le pb de définition multiple) . Ceci même si on se protège avec les #ifndef.

    Ceci implique qu'un header ne doit comporter que des déclarations.

    Une déclaration de classe est
    Une définition de classe est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Class C {...}
    Class C'{
        int x;
        int f();
    }
    Class C'' {
        int x;
        int f() {...}
    }
    D'apres la norme, on ne pourrait pas mettre dans un header les définitions précédentes...
    Ce qui est étrange c'est que la plupart des développeurs mettent les classes de types C' dans les header et font l'implémentation des fonctions dans les fichiers .cpp....

    Qu'en pensez vous?
    Ai je raté qqchose?

    Merci d'avance,
    F.

  2. #2
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Bonjour et bienvenu sur le forum

    Il faut bien quand même donner la définition au moins 1 fois.
    Ceci même si on se protège avec les #ifndef.
    Justement non, on évite les définition multiple de cette façon.

    Si tu débutes en C++, mets la norme au placard et prend un livre sur le C++ (Stroustrup)

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 4
    Par défaut
    j'ai le stroustrup, c'est une question par rapport à la norme.

    Pour le #ifndef, je ne suis pas d'accord.

    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
    soit myinclude1.h:
    #ifndef PROTECTION_INCLUDE
    #define PROTECTION_INCLUDE
    int x = 4,
    #endi PROTECTION_INCLUDE
     
    soit myinclude2.h:
    #include "myinclude1.h"
    ...
     
    soit myinclude3.h:
    #include "myinclude1.h"
    ...
     
    soit main.cpp:
    #include myinclude2.h
    #include myinclude3.h
    ....
    il y aura bien une inclusion multiple de x ???

    F.

  4. #4
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Non

    Lors du premier include, PROTECTION_INCLUDE n'est pas défini et le code interne est lu, dans le second include, PROTECTION_INCLUDE est défini et le code interne n'est pas lu.

    La norme, c'est pour ceux qui développe des compilateurs et il faut déjà connaitre le C++ pour la lire (j'exagère un peu...) Bref, à mettre au placard

  5. #5
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    La déclaration d'une classe a bien la forme class X;. Mais ce faisant, tu déclares un type qui n'est pas défini. Hors, pour qu'un type soit utilisable, dans la grande majorité des cas, il faut qu'il soit déclaré ET défini (c'est ce qu'on appelle un type complet). Si le header ne contient pas la définition de la classe, le type n'est pas instanciable, et il est donc inutilisable.

    Du coup, le header contient généralement la définition de la classe, et non pas une simple déclaration. Ce qui fait que le header ne peut pas être inclus deux fois sans header guard (car on aurait alors une violation de ODR). De même, on ne peut pas définir une autre classe portant le même nom (idem, violation de l'ODR). Par contre, à partir du moment ou la classe n'est définie qu'une seule fois dans une unité de compillation (== un .cpp), il n'y a pas de problème.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  6. #6
    Futur Membre du Club
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 4
    Par défaut
    Merci pour vos 2 réponses !

    J'avais un vieux souvenir comme quoi le header guard ne prémunissait pas tjrs de la multiple inclusions (dans des cas un peu compliqué par rapport aux inclusions en cascade de header...).

    Si effectivement le header guard assure cette garantie - il n'y a plus de problème .

    F.

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

    Informations professionnelles :
    Activité : aucun

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

    Je crois qu'il faut commencer par expliquer clairement certains termes qui semblent très semblables et qui ont pourtant des effets différents :

    Il y a
    1. La déclaration : c'est le fait de dire que quelque chose existe
    2. La définition : c'est le fait de donner corps à quelque chose
    3. L'implémentation : c'est le fait de fournir la logique interne à une fonction.
    La déclaration va simplement permettre au compilateur de savoir que quelque chose existe.

    C'est ce que l'on va utiliser, par exemple, lorsque l'on doit indiquer au compilateur qu'un type particulier existe, mais qu'il n'a, dans un premier temps, pas besoin de savoir de quoi c'est effectivement composé comme par exemple la déclaration anticipée d'une classe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class MaClass; //déclaration du type MaClass
    class Truc
    {
        public:
            void foo(MaClass * ); // la déclaration suffit ici
    };
    Il faut en effet retenir que le compilateur travaille unité de compilation par unité de compilation, et que, une fois que le préprocesseur a fini son travail, il lit le code exactement de la même manière dont toi tu lis un livre : de la première ligne à la dernière et, qu'arrivé à la ligne 10 il n'a une connaissance que de ce qu'il a rencontré dans les 9 premières lignes

    S'il rencontre, à la ligne 10 un terme qu'il ne connait pas (parce qu'il ne l'a pas rencontré dans les 9 premières lignes), il sortira sur une erreur!

    Le terme définition va prendre plusieurs sens, en fonction de la circonstance dans laquelle il est utilisé, mais tu remarqueras que l'on peut malgré tout toujours retrouver cette notion de "donner un corps à quelque chose":

    Pour une classe (ou, hormis le typedef, n'importe quel genre de type défini par l'utilisateur), il prend le sens de "fournir le contenu du type défini par l'utilisateur" : le fameux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class Truc
    {
        public:
            void foo(MaClass * ); // la déclaration suffit ici
    };
    et tout ce qui va avec est la définition de la classe Truc.

    Elle fournit ce qu'il est absolument indispensable de savoir sur la classe Truc pour être en mesure d'utiliser la classe, à savoir:
    • d'être en mesure de déterminer l'espace mémoire nécessaire pour créer une instance de la classe
    • d'être en mesure de vérifier l'existence d'une fonction que l'on tente d'appeler au départ d'une instance de la classe (ou au départ de la classe elle-même pour les fonctions statiques)
    • d'être en mesure de vérifier le prototype (type et nombre des arguments et valeur de retour) des fonctions que l'on tente d'appeler au départ d'une instance de la classe (ou au départ de la classe elle-même pour les fonctions statiques)
    La définition d'une variable est le moment où la variable commence à exister.

    En pratique, nous pouvons dire que la définition d'une variable coïncide souvent avec sa déclaration : lorsque l'on dit qu'une variable existe, le compilateur réserve un espace mémoire suffisant pour y représenter l'ensemble des données qui permettent de représenter cette variable. Il lui "donne donc un corps" par la même occasion.

    Enfin, en ce qui concerne les fonctions, la déclaration d'une fonction revient à fournir le prototype de la fonction (valeur de retour + nom + liste d'arguments éventuels ) alors que ce que l'on appelle la "définition" d'une fonction est en réalité l'implémentation de celle-ci : Lorsque l'on met en place la logique qui doit être respectée pour la fonction (AKA lorsque l'on implémente une fonction) , on "donne un corps" à la fonction : nous sommes donc bien dans le cadre d'une "définition" de fonction.

    Le One Definition Rule ( ODR ) nous dit que, dans une unité de compilation ("translation unit"), il ne peut pas apparaitre plus d'une définition pour chaque variable, type défini par l'utilisateur ou template.

    Encore faut il savoir ce que représente cette "unité de compilation".

    C'est l'ensemble constitué de :
    • un fichier d'implémentation *.cpp
    • l'ensemble des fichier d'en-tête (*.h / *.hpp ) inclus de manière directe ou indirecte par le fichier d'implémentation *.cpp
    C'est à dire que si z.cpp inclut z.h, y.h, x.h et que ces trois fichiers incluent eux même chaque fois trois fichiers (différents) d'ent-tête et que ces 9 fichiers incluent eux-même 3 fichiers d'en-tête différents, l'unité de compilation sera composée de 40 de fichiers au total (si j'ai bien compté )

    Si l'on prend l'ensemble de ces 40 fichiers, on ne peut pas trouver plus d'une définition pour chaque variable (en fonction de la portée dans laquelle elle est définie), pour chaque type défini par l'utilisateur, pour chaque fonction ou pour chaque template.

    Cela signifie que, une fois que le préprocesseur a fini de travailler, un code 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
    39
    40
    41
     
    /* Ce qui suit n'est pas une définition : ce sont des déclarations !!! */
    class Truc;
    class Brol;
    class Bidule;
    void foo(Truc *);
    void foo(Brol *);
    void foo(Bidule *);
    /* ici commencent les définitions */
    class Truc
    {
        // définition du corps de truc
    }
    class Machin
    {
        // définition du corps de machin
    }
    class Brol
    {
        // définition du corps de brol
    }
    void foo()
    {
        // implémentation de foo sans argument
    }
    void foo(Truc t )
    {
        // implémentation de foo avec un Truc comme argument
    }
    void foo(Truc * t )
    {
        // implémentation de foo avec un Truc comme argument
    }
    void foo(Machin * t )
    {
        // implémentation de foo avec un Machin comme argument
    }
    void foo(Brol * t )
    {
        // implémentation de foo avec un Brol comme argument
    }
    sera parfaitement admis, car toutes les choses ne sont jamais définies qu'une fois

    Par contre, ce qui serait interdit, c'est d'avoir
    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
    class Truc
    {
         // définition ( 1 ) du corps de Truc
    };
    /* ... */
    class Truc
    {
         // définition ( 2 ) du corps de Truc
    };
    /* ... */
     
    void foo()
    {
        // implémentation de foo sans argument
    }
    /*...*/
     
    void foo()
    {
        // implémentation de foo sans argument
    }
    Le fait que la définition 1 soit identique ou non à la définition 2 ne changera strictement rien : Truc et foo sont toutes deux définies deux fois, et c'est interdit.
    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

Discussions similaires

  1. Pré-déclaration d'une classe dans un namespace
    Par Invité dans le forum C++
    Réponses: 6
    Dernier message: 30/07/2010, 09h38
  2. Définition des class dans un même fichier!
    Par hibou107 dans le forum Entrée/Sortie
    Réponses: 2
    Dernier message: 16/04/2010, 16h30
  3. [amfPhp] Déclaration de classe dans un package
    Par luta dans le forum Dynamique
    Réponses: 6
    Dernier message: 27/09/2006, 16h37
  4. Réponses: 9
    Dernier message: 25/09/2005, 16h33
  5. Séparer la définition et la déclaration d'une classe
    Par prgasp77 dans le forum Langage
    Réponses: 5
    Dernier message: 24/08/2005, 21h37

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