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 :

Question sur les fonctions virtuelles


Sujet :

C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2012
    Messages : 15
    Points : 6
    Points
    6
    Par défaut Question sur les fonctions virtuelles
    Bonjour/bonsoir,

    Je vient (encore et encore) vous demander de l'aide non-plus a cause d'un réel problème mais d'une interrogation a propos des fonctions virtuelle. Tout simplement comment fonctionnent-t'elle? J'ai fait un tour sur la sacro-sainte FAQ mais j'avoue ne pas vraiment comprendre.

    Lorsque on a une classe B héritée d'une classe A, qu'on a déclarer un objet A mais qu'on l'a initialiser comment étant un B et qu'on souhaite appeler une fonction de B qui dérive de A il suffit de déclarer la fonction (dans la classe A) avec le mot-clef "virtual" devant non?

    C'est ce que je fait mais... ca fonctionne pas, ca appelle la fonction de l'objet A. Je voudrait juste savoir si le mot-clef "virtual" est suffisant ou il faut faire autre chose.(Je ne donne pas de code source car j'ai juste besoin d'informations théoriques).
    Merci pour votre précieuse aide!

  2. #2
    Membre éclairé

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Points : 877
    Points
    877
    Billets dans le blog
    1
    Par défaut
    Bonjour,
    Lorsque on a une classe B héritée d'une classe A, qu'on a déclarer un objet A mais qu'on l'a initialiser comment étant un B et qu'on souhaite appeler une fonction de B qui dérive de A il suffit de déclarer la fonction (dans la classe A) avec le mot-clef "virtual" devant non?
    Comment as tu déclaré l'objet A ? Normalement ta classe A n'a pas à être déclarée, ses classes héritées le seront.

    Ce petit encadré décrit parfaitement l'utilité et la forme de l'héritage, et le mot "virtual", il sert en quelque sorte à envoyer une fonction de ta classe héritée aux classes "enfant".

  3. #3
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    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
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par Negatio Voir le message
    Bonjour/bonsoir,

    Je vient (encore et encore) vous demander de l'aide non-plus a cause d'un réel problème mais d'une interrogation a propos des fonctions virtuelle. Tout simplement comment fonctionnent-t'elle? J'ai fait un tour sur la sacro-sainte FAQ mais j'avoue ne pas vraiment comprendre.
    Toute la suite s'entends de manière schématique. Les implémentations peuvent être différentes, et vont aussi rajouter des points importants (comme la gestion des dynamic_cast<>).

    Lorsqu'un objet de type A est créé, le compilateur réserve sizeof(A) bytes pour le stocker. Si A ne contient pas de fonction virtuelle, alors
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      sizeof(A) = (somme des tailles de toutes les données membres de A)
    En mémoire, l'objet de type A est stocké sous la forme suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    adresse de l'instance de A: 
       donnée 1 (taille N)
       donnée 2 (taille P)
       ...
    Si A contient au moins une fonction virtuelle, alors
    [code]
    sizeof(A) = (somme des tailles de toutes les données membres de A) + sizeof(void*)
    [code]

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    adresse de l'instance de A: 
       pointeur vers la table de fonctions virtuelle (vtable)
       donnée 1 (taille N)
       donnée 2 (taille P)
       ...
    La vtable est commune à toutes les instance du même type dynamique (ce qui veut dire que les objets de type B dérivant de A ont une vtable différente, même si on y accède à partir d'une variable dont le type statique est A. Donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class B : public A { ... }
     
    A a; // vtable == vtable de A
    B b; // vtable == vtable de B
    A* ab = &b; // vtable == vtable de B
    La vtable est simplement une table de pointeurs sur fonction (plus exactement, de pointeurs sur méthode). Chaque entrée de la table correspond à une fonction virtuelle de la classe. L'ordre des fonctions dans la vtable est celui donnée dans le fichier source de la classe mère. Pour les classes dérivées, les nouvelles fonctions virtuelles sont ajoutée ensuite. Un exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    struct A
    {
      virtual void f1();
      virtual void f2();
    };
     
    struct B : A
    {
      virtual void f3();
      virtual void f1(); // surcharge de A::f1()
    };
    On a les vtables suivantes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    pseudo-type-dépendant-du-compilateur A::vtable[] = {
      &A::f1,
      &A::f2
    };
     
    pseudo-type-dépendant-du-compilateur B::vtable[] = {
      &B::f1,
      &A::f2,
      &B::f3
    };
    Il ne reste plus qu'à accéder à ces fonctions. Comment fait-on ? C'est en fait très, très simple. Chaque objet a une adresse. Lorsqu'on accède à un membre de l'objet, cette adresse est mappée sur le pointeur this. Du coup, le compilateur ne fait que transcrire en assembleur la ligne de code suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
      // B b; 
      // A *ab = &b;
      // ab->f1();
      this->vtable[index de f1() dans la table]
    L'index est connu au moment de la compilation - donc le compilateur peut aisément accéder à la fonction souhaitée.

    Citation Envoyé par Negatio Voir le message
    Bonjour/bonsoir,

    Lorsque on a une classe B héritée d'une classe A, qu'on a déclarer un objet A mais qu'on l'a initialiser comment étant un B et qu'on souhaite appeler une fonction de B qui dérive de A il suffit de déclarer la fonction (dans la classe A) avec le mot-clef "virtual" devant non?

    C'est ce que je fait mais... ca fonctionne pas, ca appelle la fonction de l'objet A. Je voudrait juste savoir si le mot-clef "virtual" est suffisant ou il faut faire autre chose.(Je ne donne pas de code source car j'ai juste besoin d'informations théoriques).
    Merci pour votre précieuse aide!
    D'abord, quelques pitites définitions :

    Le type statique est le type de l'objet tel que vu par le compilateur. Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    A a; // type statique : A
    A *a; // type statique: A*
    Le type dynamique (attention pour les puristes : je fait une simplification outrancière, histoire d'expliquer le truc) est le type réel de l'objet, tel qu'il a été créé. Cependant, le type dynamique ne survit pas à ce qu'on appelle le slicing - c'est à dire le fait de copier un objet d'un type donné dans un objet d'un type parent.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    A a:
    B b; // B dérive de A
    a = b; // copie ==> slicing ==> le type dynamique de a est A.
    De fait, puisque le slicing détruit le type dynamique, on ne parle pas de type dynamique dans ce cas (note pour les puristes : l'honneur est sauf, hein ?). On réserve cette locution aux types pointé via un pointeur ou une référence (du point de vue du code généré, ces deux entités se comportent de manière très similaires).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    A *a = &b; // type statique: A* ; type dynamique : B*
    Maintenant, la conclusion : pour qu'une donnée de type statique A puisse appeler une fonction surchargée dans une classe dérivée, il faut que le type dynamique de l'objet soit cette classe dérivée.

    Concrètement, il faut passer par un pointeur ou une référence.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    B b;
    A a = b; 
    a.f1(); // appelle A::f1();
    A *ab = &b;
    ab->f1(); // appelle B::f1();
    [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.

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    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 186
    Points : 17 126
    Points
    17 126
    Par défaut
    J'en profite pour t'informer d'un détail subtil.
    Tu risques un jour de rencontrer une erreur "unable to find symbol for `vtable' for type Machin"
    Ce message signifie que l'éditeur de liens n'a pas trouvée cette fameuse table de fonctions virtuelles.

    La raison est que cette table n'est correctement définie que si (au moins) une fonction virtuelle n'est pas inline, c'est-à-dire qu'elle est définie dans un fichier .cpp, non dans un en-tête (dans la déclaration de la classe ou avec le mot clé inline).
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    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
    Points : 4 551
    Points
    4 551
    Par défaut
    A titre indicatif, je viens de trouver un article traitant du même sujet sur embedded.com

    Storage layout for polymorphic objects

    Un autre article peut aussi être intéressant à lire :

    Virtual functions in C.

    Enfin, histoire de voir comment c'est implémenté en pratique, je conseille la lecture du code source de cfront, le compilateur C++ originel (créé par Bjarne Stroustrup lui-même). cfront transforme du code C++ en code C (attention toutefois : la dernière version date de 1991 et il lui manque un certain nombre de choses - comme les exceptions, etc...).
    [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
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2012
    Messages : 15
    Points : 6
    Points
    6
    Par défaut
    Tout d'abord merci a vous pour ces réponses très complètes!

    Comment as tu déclaré l'objet A ? Normalement ta classe A n'a pas à être déclarée, ses classes héritées le seront.
    Je ne comprend pas vraiment ce que tu veut dire. J'ai une classe quelconque, dans son header j'ai écrit:
    Et puis dans le constructeur de cette classe quelconque j'ai fait:
    Je voit pas ce que tu veut dire par "déclarer une classe", il faut que je créer la classe A pour que B puisse en hérité non?

    La vtable...
    J'ai vraiment mal chercher je pense pars-ce-que j'ai rien trouver de semblable. Je savait pas que ca exister.

    Concrètement, il faut passer par un pointeur ou une référence.
    Encore eux! Je me suit jamais douter qu'il faut utiliser ça.

    J'en profite pour t'informer d'un détail subtil.
    Tu risques un jour de rencontrer une erreur "unable to find symbol for `vtable' for type Machin"
    Ce message signifie que l'éditeur de liens n'a pas trouvée cette fameuse table de fonctions virtuelles.

    La raison est que cette table n'est correctement définie que si (au moins) une fonction virtuelle n'est pas inline, c'est-à-dire qu'elle est définie dans un fichier .cpp, non dans un en-tête (dans la déclaration de la classe ou avec le mot clé inline).
    Je m'en rappellerais (peut-être) en temps voulu.

    A titre indicatif, je viens de trouver un article traitant du même sujet sur embedded.com

    Storage layout for polymorphic objects

    Un autre article peut aussi être intéressant à lire :

    Virtual functions in C.
    Je vais surpasser mon anglophobie pour lire ça

    Enfin, histoire de voir comment c'est implémenté en pratique, je conseille la lecture du code source de cfront, le compilateur C++ originel (créé par Bjarne Stroustrup lui-même). cfront transforme du code C++ en code C (attention toutefois : la dernière version date de 1991 et il lui manque un certain nombre de choses - comme les exceptions, etc...).
    Ca existe ça?! Bon ben je vais y jeter un gros coup d'oeuil.

    Encore merci énormément pour vos précieux conseils.

  7. #7
    Membre éclairé

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Points : 877
    Points
    877
    Billets dans le blog
    1
    Par défaut
    Bonjour,
    Citation:
    Concrètement, il faut passer par un pointeur ou une référence.
    Encore eux! Je me suit jamais douter qu'il faut utiliser ça.
    Ces notions de pointeurs et références devront être très clair pour entamer la suite !

  8. #8
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    A titre indicatif, je viens de trouver un article traitant du même sujet sur embedded.com

    Storage layout for polymorphic objects

    Un autre article peut aussi être intéressant à lire :

    Virtual functions in C.

    Enfin, histoire de voir comment c'est implémenté en pratique, je conseille la lecture du code source de cfront, le compilateur C++ originel (créé par Bjarne Stroustrup lui-même). cfront transforme du code C++ en code C (attention toutefois : la dernière version date de 1991 et il lui manque un certain nombre de choses - comme les exceptions, etc...).
    C'est dommage qu'il n'y rien de plus récent.
    Je suis curieux de voir la compil en C d'un code C++11 avec des lambda, des move reference et autres joyeusetés.

  9. #9
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2012
    Messages : 15
    Points : 6
    Points
    6
    Par défaut
    Ces notions de pointeurs et références devront être très clair pour entamer la suite !
    Je sait bien mais je préférais faire sans. Je comprend la théorie mais en pratique ca me pose toujours quelque problème. Enfin, faut bien que je passe par là!

  10. #10
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    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
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par camboui Voir le message
    C'est dommage qu'il n'y rien de plus récent.
    Je suis curieux de voir la compil en C d'un code C++11 avec des lambda, des move reference et autres joyeusetés.
    Ah mais tu peux !

    Je te conseille de checker LLVM - avec ça, tu peux construire un compilateur - et tu peux surtout instrumenter le code qui permet de construire un compilateur.
    [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.

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Negatio Voir le message
    Je sait bien mais je préférais faire sans. Je comprend la théorie mais en pratique ca me pose toujours quelque problème. Enfin, faut bien que je passe par là!
    Il est impossible de faire sans.
    La virtualité et le polymorphisme fait appel au type dynamique de l'objet. Non au type statique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    A myA; // type statique A, type dynamique A
    B myB; // type statique B, type dynamique B
    A* pA = &myA; // type statique A, type dynamique A
    pA = &myB; // type statique A, type dynamiqye B
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

Discussions similaires

  1. Question sur les fonctions virtuelles
    Par beegees dans le forum C++
    Réponses: 9
    Dernier message: 13/04/2008, 20h54
  2. Question sur les fonctions ORACLE
    Par Fakhry dans le forum SQL
    Réponses: 8
    Dernier message: 12/12/2006, 18h13
  3. [Dates] Question sur les fonctions Date
    Par cerede2000 dans le forum Langage
    Réponses: 2
    Dernier message: 28/10/2006, 18h26
  4. Réponses: 2
    Dernier message: 20/10/2006, 16h07
  5. Question sur les fonctions "send()" et "recv(
    Par damien99 dans le forum MFC
    Réponses: 6
    Dernier message: 10/02/2006, 21h47

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