Quand un vtable est-il créé en C++?

quand exactement le compilateur crée-t-il une table de fonction virtuelle?

1) lorsque la classe contient au moins une fonction virtuelle.

ou

2) lorsque la classe de base immédiate contient au moins une fonction virtuelle.

ou

3) lorsqu'une classe mère à un niveau quelconque de la hiérarchie contient au moins une fonction virtuelle.

une question connexe à ce: Est-il possible d'abandonner la régulation dynamique dans une hiérarchie c++?

par exemple, prenons l'exemple suivant.

#include <iostream>
using namespace std;
class A {
public:
  virtual void f();
};
class B: public A {
public:
  void f();
};
class C: public B {
public:
  void f();
};

quelles classes contiendront une Table en V?

puisque B ne déclare pas f () comme virtuel, est-ce que la Classe C obtient le polymorphisme dynamique?

23
demandé sur Aquarius_Girl 2009-12-26 21:02:01

6 réponses

au-delà "les vtables sont spécifiques à l'implémentation" (ce qu'ils sont), si un vtable est utilisé: il y aura des vtables uniques pour chacune de vos classes. Même si B::f et c::f ne sont pas déclarés virtuels, parce qu'il y a une signature correspondante sur une méthode virtuelle à partir d'une classe de base ( A dans votre code), B::f et c::f sont tous deux implicitement virtuels. Parce que chaque classe A au moins une méthode virtuelle unique ( B::f overrides A::F pour B instances et C::f de même pour c instances), vous avez besoin de trois vtables.

vous ne devriez généralement pas vous soucier de tels détails. Ce qui importe est de savoir si vous avez virtual dispatch ou non. vous ne devez pas utiliser Virtual dispatch, par spécifier explicitement quelle fonction appeler, mais cela n'est généralement utile que lors de la mise en œuvre d'une méthode virtuelle (comme appeler la méthode de la base). Exemple:

struct B {
  virtual void f() {}
  virtual void g() {}
};

struct D : B {
  virtual void f() { // would be implicitly virtual even if not declared virtual
    B::f();
    // do D-specific stuff
  }
  virtual void g() {}
};

int main() {
  {
    B b; b.g(); b.B::g(); // both call B::g
  }
  {
    D d;
    B& b = d;
    b.g(); // calls D::g
    b.B::g(); // calls B::g

    b.D::g(); // not allowed
    d.D::g(); // calls D::g

    void (B::*p)() = &B::g;
    (b.*p)(); // calls D::g
    // calls through a function pointer always use virtual dispatch
    // (if the pointed-to function is virtual)
  }
  return 0;
}

quelques règles concrètes qui peuvent aider; mais ne me citez pas sur ces, j'ai probablement manqué quelques cas de bord:

  • si une classe a des méthodes virtuelles ou des bases virtuelles, même héritées, alors les instances doivent avoir un pointeur vtable.
  • si une catégorie déclare des méthodes virtuelles non héritées (comme quand il n'a pas de classe de base), alors il doit avoir son propre vtable.
  • si une classe a un ensemble différent de méthodes de surpassement que sa première classe de base, alors elle doit avoir son propre vtable, et ne peut pas réutiliser la base. (Les destructeurs en ont souvent besoin.)
  • si une classe a plusieurs classes de base, avec la seconde ou une base ultérieure ayant des méthodes virtuelles:
    • Si pas plus tôt bases ont des méthodes virtuelles et L'optimisation de base vide a été appliquée à toutes les bases précédentes, puis traiter cette base comme la première classe de base.
    • sinon, la classe doit avoir son propre vtable.
  • si une classe a des classes de base virtuelles, elle doit avoir sa propre vtable.

rappelez-vous qu'un vtable est similaire à un membre de données statique d'une classe, et les instances n'ont que des pointeurs vers ceux-ci.

Voir aussi l'article complet C++: sous le capot (mars 1994) par Jan Gray. ( Essayez Google si ce lien meurt.)

exemple de réutilisation d'un vtable:

struct B {
  virtual void f();
};
struct D : B {
  // does not override B::f
  // does not have other virtuals of its own
  void g(); // still might have its own non-virtuals
  int n; // and data members
};

en particulier, l'avis B 's dtor n'est pas virtuel( et ce est probablement une erreur en code réel ), mais en cet exemple, D instances pointera vers le même tableau que B instances.

23
répondu 2010-04-17 20:09:48

la réponse est, 'cela dépend'. Cela dépend de ce que vous entendez par "contenir une vtbl" et cela dépend des décisions prises par l'exécuteur du compilateur particulier.

strictement parlant, aucune "classe" ne contient jamais de table de fonction virtuelle. Certaines classes contiennent des pointeurs vers des tables de fonctions virtuelles. Cependant, ce n'est qu'une implémentation possible de la sémantique.

À l'extrême, un compilateur pourrait hypothétiquement mettre un numéro unique dans l'instance qui s'est indexée dans une structure de données utilisée pour sélectionner l'instance de fonction virtuelle appropriée.

si vous demandez, " Que fait GCC?"ou" que fait Visual C++?'ensuite, vous pourriez obtenir une réponse concrète.

la réponse de @Hassan Syed est probablement plus proche de ce que vous demandiez, mais il est vraiment important de garder les concepts clairs ici.

il y a comportement (répartition dynamique basé sur ce que la classe a de nouvelles ed) et il y a mise en œuvre . Votre question utilisait la terminologie de l'implémentation, bien que je soupçonne que vous cherchiez une réponse comportementale.

la réponse comportementale est la suivante: toute classe qui déclare ou hérite d'une fonction virtuelle montrera un comportement dynamique sur les appels à cette fonction. Toute classe qui n'est pas, ne sera pas.

implémentation-wise, le compilateur est autorisé à faire ce qu'il veut accomplir ce résultat.

8
répondu bmargulies 2009-12-26 20:06:28

réponse

une vtable est créé lorsqu'une déclaration de classe contient une fonction virtuelle. Un vtable est introduit quand un parent -- n'importe où dans l'hérarchie -- a une fonction virtuelle, appelons ce parent Y. N'importe quel parent de Y N'aura pas de vtable (à moins qu'ils aient un virtual pour une autre fonction dans leur hérarchie).

Lire la suite pour discussion et essais

-- explication --

lorsque vous spécifiez qu'une fonction membre est virtuelle, il y a une chance que vous essayiez d'utiliser des sous-classes via une classe de base polymorphique à l'exécution. Pour maintenir la garantie de performance de c++sur la conception du langage, ils ont offert la stratégie de mise en œuvre la plus légère possible, c'est-à-dire un niveau de mise en œuvre indirecte, et seulement quand une classe peut être utilisée polymorphiquement à l'exécution, et le programmeur spécifie ceci par la définition d'au moins une fonction virtuelle.

vous n'engagez pas le coût du vtable si vous évitez le mot-clé virtuel.

-- Modifier: pour tenir compte de votre modifier --

ce N'est que lorsqu'une classe de base contient une fonction virtuelle que d'autres sous-classes contiennent un vtable. Les parents de cette classe de base n'ont pas de table.

dans votre exemple, les trois classes auront une vtable, c'est parce que vous pouvez essayer d'utiliser les trois classes via un A*.

--test-GCC 4+ --

#include <iostream>

class test_base
{
  public:
    void x(){std::cout << "test_base" << "\n"; };
};

class test_sub : public test_base
{
public:
  virtual void x(){std::cout << "test_sub" << "\n"; } ;
};

class test_subby : public test_sub
{
public:
  void x() { std::cout << "test_subby" << "\n"; }
};

int main() 
{
  test_sub sub;
  test_base base;
  test_subby subby;

  test_sub * psub;
  test_base *pbase;
  test_subby * psubby;

  pbase = &sub;
  pbase->x();
  psub = &subby;
  psub->x();

  return 0;
}

sortie

test_base
test_subby

test_base n'a pas de table virtuelle donc tout ce qui y est placé utilisera le x() de test_base . test_sub d'autre part change la nature de x() et son pointeur sera indirecte à travers un vtable, et ceci est montré par test_subby 's x() étant exécuté.

ainsi, un vtable n'est introduit dans la hiérarchie que lorsque le mot-clé virtuel est utilisé. Les ancêtres plus âgés n'ont pas de vtable, et si un downcast se produit, il sera relié aux fonctions des ancêtres.

7
répondu Hassan Syed 2009-12-26 20:16:19

vous avez fait un effort pour rendre votre question très claire et précise, mais il y a encore un peu d'information manquante. Vous savez probablement, que dans les implémentations qui utilisent V-Table, la table elle-même est normalement une structure de données indépendante, stockée en dehors des objets polymorphiques, alors que les objets eux-mêmes ne stockent qu'un pointeur implicite à la table. Alors, qu'est-ce que vous me demandez? Pourrait être:

  • quand un objet obtient-il un pointeur implicite vers la Table en V?

ou

  • quand un dédié, individuel V-Table créé pour un type donné dans la hiérarchie?

la réponse à La première question est: un objet reçoit un implicite pointeur vers V-Tableau inséré lorsque l'objet est d'polymorphes type de classe. Le type de classe est polymorphe s'il contient au moins un fonction, ou l'un de ses parents directs ou indirects sont polymorphiques (c'est la réponse 3 de votre ensemble). Notez aussi que dans le cas d'un héritage multiple, Un objet pourrait (et pourrait) finir par contenir plusieurs pointeurs de Table en V incrustés dedans.

la réponse à la deuxième question pourrait être la même que pour la première (option 3), avec une exception possible. Si une classe polymorphique de la hiérarchie de l'héritage unique n'a pas de fonctions virtuelles propres (pas de nouvelles fonctions virtuelles, pas de overrides for parent virtual function), il est possible que l'implémentation décide de ne pas créer une Table V individuelle pour cette classe, mais utilise plutôt la Table V du parent immédiat pour cette classe aussi (puisque ce sera la même de toute façon). C'est-à-dire: dans ce cas, les objets de type parent et les objets de type dérivé stockeront la même valeur dans leurs pointeurs de Table en V intégrés. Cela dépend évidemment beaucoup de la mise en œuvre. J'ai vérifié GCC et MS VS 2005 et ils n'agissent pas comme ça. façon. Ils créent tous les deux une Table V individuelle pour la classe dérivée dans cette situation, mais je crois me souvenir d'avoir entendu parler d'implémentations qui ne le font pas.

3
répondu AnT 2009-12-26 18:58:29

normes C++ n'a pas de mandat de l'utilisation de V-Tableaux pour créer la illusion des classes polymorphes. La plupart du temps, les implémentations utilisent des Tables en V, pour stocker les informations supplémentaires nécessaires. En bref, ces informations supplémentaires sont équipées lorsque vous avez au moins une fonction virtuelle.

2
répondu AraK 2009-12-26 18:15:11

le comportement est défini au chapitre 10.3, paragraphe 2 de la spécification du langage C++:

si une fonction de membre virtuel vf est déclaré dans une base de classe et dans un classe Dérivée, dérivée directement ou indirectement à partir de la Base, un membre fonction vf avec le même nom et même Liste de paramètres que la Base:: vf is déclaré, puis Dérivé::vf est également virtuel ( qu'il en soit ainsi ou non déclaré ) et il l'emporte sur la Base:: vf.

a souligné la phrase pertinente. Ainsi, si votre compilateur crée des v-tables dans le sens habituel, alors toutes les classes auront une v-table puisque toutes leurs méthodes f() sont virtuelles.

1
répondu Hans Passant 2009-12-26 19:01:30