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 :

Héritage et polymorphisme en C


Sujet :

C

  1. #1
    Futur Membre du Club
    Inscrit en
    Septembre 2010
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 7
    Points : 9
    Points
    9
    Par défaut Héritage et polymorphisme en C
    Bonjour,

    Dans son tutoriel sur l'héritage en C, http://chgi.developpez.com/c/heritage/ , CGi utilise la méthode suivante:
    Quand on veut qu'une structure hérite d'une autre structure, le début de la classe fille doit être le même que celui de la classe mère. Les nouveaux attributs sont ajoutés à la fin.

    Exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    struct Mere {
       int a;
       int b;
    };
     
    struct Fille {
       // membres de Mere
       int a;
       int b;
       // membres de Fille
       int c;
    }
    Il utilise le polymorphisme:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Mere *m;
    struct Fille f = { 1, 2, 3 };
     
    m = (struct Mere *) &f;
    // utiliser m->a et m->b
    Le C garantit que le premier élément de la structure est à l'addresse 0, donc ça doit marcher pour "a" tout le temps.
    Ma question est: est-ce que pour les autres éléments de la structure, ça marchera quelque soit le type et le nombre d'attributs, et quelque soit le compilateur ? Parce que le compilateur peut également ajouter des octets de remplissage (padding).

  2. #2
    Membre à l'essai
    Homme Profil pro
    autodidacte
    Inscrit en
    Mai 2015
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : autodidacte
    Secteur : Finance

    Informations forums :
    Inscription : Mai 2015
    Messages : 16
    Points : 12
    Points
    12
    Par défaut
    Pourquoi ne pas passer au C++ ?

    Sinon, il y a les struct, les énum. Et ne pas utiliser de variables globales.

  3. #3
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Je te déconseille ce genre de code. Si les optimisations de compilation en vectorisation sont utilisées, ton code ne fonctionnera probablement plus.
    Exemple avec xlC, le compilateur IBM AIX :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct
    {
            int a;
            int b;
    } premier;
     
    typedef struct
    {
            int a;
            int b;
            char c;
    } second;
     
    int main( void )
    {
            second s;
            s.a = 10;
            s.b = 40;
            s.c = 2;
            premier * p = (premier *)((void*)& s);
            printf( "a=%d, b=%d\n", p->a, p->b );
            exit( 0 );
    }
    Ce code fonctionne très bien en compilant avec xlC sans aucune option.
    Par contre, en compilant avec : xlc -O3 -qarch=pwr6 -qsimd=auto mon_prog.c, l'affichage devient faux par rapport à ce qu'on attend.

  4. #4
    Membre expérimenté

    Homme Profil pro
    Collégien
    Inscrit en
    Juillet 2010
    Messages
    559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Juillet 2010
    Messages : 559
    Points : 1 432
    Points
    1 432
    Par défaut
    Quand on veut qu'une structure hérite d'une autre structure, le début de la classe fille doit être le même que celui de la classe mère
    Je ne trouve pas çà très malin... Si tu dois modifier ton objet mère, tu dois faire la même modif sur tous les objet qui en hérite...

    Je préfère comme ca:

    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
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct
    {
            int a;
            int b;
    } premier;
     
    typedef struct
    {
            premier p;
            char c;
    } second;
     
    int main( void )
    {
            second s;
            s.p.a = 10;
            s.p.b = 40;
            s.c = 2;
            premier * p = &s.p;
            printf( "a=%d, b=%d\n", p->a, p->b );
            exit( 0 );
    }

  5. #5
    Membre éclairé
    Inscrit en
    Juillet 2012
    Messages
    231
    Détails du profil
    Informations forums :
    Inscription : Juillet 2012
    Messages : 231
    Points : 870
    Points
    870
    Par défaut
    Citation Envoyé par dinobogan
    Si les optimisations de compilation en vectorisation sont utilisées, ton code ne fonctionnera probablement plus.
    Probablement car la règle de l’aliasing est violée.

    Citation Envoyé par dinobogan
    Par contre, en compilant avec : xlc -O3 -qarch=pwr6 -qsimd=auto mon_prog.c, l'affichage devient faux par rapport à ce qu'on attend.
    Par curiosité, ça affiche quoi ?


    Citation Envoyé par mith06
    Je ne trouve pas çà très malin... Si tu dois modifier ton objet mère, tu dois faire la même modif sur tous les objet qui en hérite...
    Yep, c’est vraiment un handicap pour quand le code évolue (en plus du problème d’aliasing).

    Citation Envoyé par mith06
    Je préfère comme ca:
    Code C : 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
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct
    {
            int a;
            int b;
    } premier;
     
    typedef struct
    {
            premier p;
            char c;
    } second;
     
    int main( void )
    {
            second s;
            s.p.a = 10;
            s.p.b = 40;
            s.c = 2;
            premier * p = &s.p;
            printf( "a=%d, b=%d\n", p->a, p->b );
            exit( 0 );
    }
    En plus d‘une meilleure maintenabilité, ça devrait résoudre le problème observé par dinobogan.

    @dinobogan : tu peux tester le code de mith06 avec xlC pour voir si tu as toujours un résultat faux ?

  6. #6
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Bonjour,

    Personnellement je pense que cet article sur la POO en C n'est pas celui qu'il faut rédiger/lire, si le but est d'introduire les programmeurs à la POO.

    On ne peut pas introduire un paradigme en partant du paradigme en question, où la scission/transition dans ce cas?

    Le C n'est pas fait pour de l'objet, c'est un langage impératif et c'est comme cela que ça marche.

    En revanche, une meilleure façon de faire passer les programmeurs du C au C++ est une introduction et un apprentissage du C modulaire, qui est d'ailleurs largement adopté dans le développement logiciel en C, le code du noyeau linux par exemple.

    Le fond de l'article n'est pas mal, mais je le reprendrais bien en un cours sur le C modulaire, qui ferait rester le lecteur dans le C et le paradigme associé, en lui montrant une meilleure approche de la POO/C++.

    Je sais que mon post ne répond pas à la question posé, mais je ne n'ai pas vu où laisser un commentaire sur la page du cours en question.

    Bien cordialement

  7. #7
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Citation Envoyé par grim7reaper Voir le message
    Par curiosité, ça affiche quoi ?
    Ca affiche : a=804396748, b=804396756
    Ces deux valeurs ne changent pas, même après avoir changer les affectations '10' et '40'.
    @dinobogan : tu peux tester le code de mith06 avec xlC pour voir si tu as toujours un résultat faux ?
    Ce code fonctionnera sans problème. Mais je teste quand même...
    Après test, ça fonctionne sans problème. Et heureusement, sinon on ne pourrait plus utiliser les structures

  8. #8
    Futur Membre du Club
    Inscrit en
    Septembre 2010
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 7
    Points : 9
    Points
    9
    Par défaut
    dinobogan,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    premier * p = (premier *)((void*)& s);
    A quoi sert le premier cast ici ? (void *)

    PS: La norme C garantit que l'adresse d'une structure est l'adresse de son premier élément, donc p->a devrait valoir 10.

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 837
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 837
    Points : 996
    Points
    996
    Par défaut
    Citation Envoyé par enclair Voir le message
    dinobogan,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    premier * p = (premier *)((void*)& s);
    A quoi sert le premier cast ici ? (void *)
    A priori, à rien...

  10. #10
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Citation Envoyé par enclair Voir le message
    dinobogan,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    premier * p = (premier *)((void*)& s);
    A quoi sert le premier cast ici ? (void *)
    Sans le "void*", le compilateur sort les informations de compilations suivantes :
    (I) Pointer type conversion found.
    (I) Pointer types "struct {...}*" and "struct {...}*" are not compatible.

  11. #11
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Le compilateur xLC sur IBM AIX est souvent casse-pied lorsqu'on utilise une optimisation de compilation supérieure ou égale à -O3 et l'option -qsimd=auto.
    Voici deux codes qui ne donnent pas le même résultat en compilant avec ces deux options :

    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
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct { int a; int b; } premier;
    typedef struct { int a; int b; char c; } second;
     
    int main( void )
    {
        second s = { 0 };
        s.a = 10;
        s.b = 40;
        s.c = 2;
        premier * p = (premier*)((void*)(& s));
        printf( "par pointeur : a=%d, b=%d\n", p->a, p->b );
     
        exit( 0 );
    }
    résultat :
    par pointeur : a=10, b=40
    Donc OK, tout va bien.

    Maintenant, supprimons l'initialisation de la structure :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct { int a; int b; } premier;
    typedef struct { int a; int b; char c; } second;
     
    int main( void )
    {
        second s;
        s.a = 10;
        s.b = 40;
        s.c = 2;
        premier * p = (premier*)((void*)(& s));
        printf( "par pointeur : a=%d, b=%d\n", p->a, p->b );
     
        exit( 0 );
    }
    résultat :
    par pointeur : a=804396732, b=804396740
    Et là c'est le drame...

    Mais puisque ces options de compilations permettent de diminuer de beaucoup le temps d'exécution de mes programmes, je compile avec ces options mais je suis devenu très pointilleux sur l'usage des structures et de leurs membres.
    Si quelqu'un a une explication sur ce comportement, je suis preneur car la doc officielle IBM ne dit quasiment rien sur les effets concrets des options de compilations pour l'optimisation du code. Je n'ai trouvé aucun exemple avec explication décrivant ce genre de phénomène.

  12. #12
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 195
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 195
    Points : 17 163
    Points
    17 163
    Par défaut
    La norme prévoit que toute variable lue sans avoir été initialisée est un "undefined behaviour" (voire un "undetermined behaviour"...) c'est à dire que tu ne peux pas t'y fier, et que le compilateur est libre de son choix.

    Du coup, c'est logique que si tu supprimes les initialisations, tu ne saches pas ce que tu lis.

  13. #13
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Citation Envoyé par leternel Voir le message
    La norme prévoit que toute variable lue sans avoir été initialisée est un "undefined behaviour" (voire un "undetermined behaviour"...) c'est à dire que tu ne peux pas t'y fier, et que le compilateur est libre de son choix.
    Oui, on est bien d'accord. Seulement mais variables ont été initialisées, certes pas lors de la déclaration, mais juste après, membre par membre.
    Du temps ou j'utilisais gcc, je n'avais jamais eu ce genre de mésaventure

  14. #14
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 195
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 195
    Points : 17 163
    Points
    17 163
    Par défaut
    En ouvrant les yeux, c'est mieux.

    En effet, c'est quelque peu étrange.
    D'un autre côté, une paire de cast, ca s'appelle violer le système de type.
    Que, dans une telle situation, ton compilateur t'envoie bouler me semble pour le moins ... légitime.

    Je suis prêt à parier que c'est un problème d'alignement des pointeurs et des pointés.

  15. #15
    Membre éclairé
    Inscrit en
    Juillet 2012
    Messages
    231
    Détails du profil
    Informations forums :
    Inscription : Juillet 2012
    Messages : 231
    Points : 870
    Points
    870
    Par défaut
    @dinobogan : merci pour le test. En effet, si le second code (celui de mith06) donnait aussi un résultat erronné cela serait très problématique.

    Citation Envoyé par dinobogan
    Si quelqu'un a une explication sur ce comportement, je suis preneur car la doc officielle IBM ne dit quasiment rien sur les effets concrets des options de compilations pour l'optimisation du code.
    Mécanique interne du compilateur, et c’est en effet rarement documenté (voir impossible à documenter).
    L’initialisation des membres à 0 de la structure doit influer sur les analyses de flots et/ou sur les optimisations possibles par le compilateurs.
    Clairement, les deux codes sont « faux » car il y a la ligne :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    premier * p = (premier*)((void*)(& s));
    Car cela viole la règle de l’aliasing (comme je l’ai dit dans un message précédent), et c’est un truc très utilisé par les compilateurs pour optimiser justement.

    Un très bon article sur cette règle du strict aliasing.

  16. #16
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Citation Envoyé par grim7reaper Voir le message
    Clairement, les deux codes sont « faux » car il y a la ligne :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    premier * p = (premier*)((void*)(& s));
    Car cela viole la règle de l’aliasing (comme je l’ai dit dans un message précédent), et c’est un truc très utilisé par les compilateurs pour optimiser justement.
    Merci pour l'article. J'ai recompilé le programme qui donne un résultat inattendu en ajoutant l'option -qalias=noansi, et le résultat devient correct.
    C'était donc bien un problème d'aliasing.

    Comme déjà dit (pour repartir sur le sujet initial de la discussion), il ne faut pas utiliser ce genre de code. "L'héritage" de structure comme présenté dans le premier post est une très mauvaise pratique.

  17. #17
    Nouveau Candidat au Club
    Homme Profil pro
    Architecte de système d'information
    Inscrit en
    Août 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Architecte de système d'information

    Informations forums :
    Inscription : Août 2015
    Messages : 2
    Points : 1
    Points
    1
    Par défaut
    Bonjour, l utilisation "d heritage" (structure enfant debutant par la declaration d une instance de la structure mere est largement utilisee en C sans aucun probleme et est une bonne technique (cf kernel). Le probleme de valeur erronee dont vous vous faites echo semble plutot du a un probleme d alignement de structure...

Discussions similaires

  1. héritage et polymorphisme
    Par julien.metais dans le forum Hibernate
    Réponses: 3
    Dernier message: 17/05/2009, 09h58
  2. Réponses: 10
    Dernier message: 17/07/2008, 20h01
  3. héritage et polymorphisme
    Par davdou dans le forum JSF
    Réponses: 2
    Dernier message: 23/11/2007, 09h51
  4. [C#] Information sur héritage et polymorphisme
    Par LE NEINDRE dans le forum C#
    Réponses: 21
    Dernier message: 14/06/2007, 11h00
  5. Réponses: 19
    Dernier message: 05/06/2007, 08h13

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