0
votes

Le pointeur de la classe de base pointant sur une classe dérivée ne peut pas accéder à la méthode de classe dérivée

J'apprends l'héritage dans C ++ 11 et j'ai constaté que si une classe dérivée a redéfini d'un nom de fonction virtuel mais avec un prototype différent, un pointeur de classe de base attribué à un pointeur à la classe dérivée ne peut accéder à la Version de la classe de base de la fonction. La fonction de version dérivée n'est pas accessible. Je me demande pourquoi cela se produit.

Dragon foo;
Enemy* pe = &foo;
pe->describe(); // Enemy
foo.describe(1); // Dragon
pe->describe(1); // no matching function, candidate is Enemy::describe()


2 commentaires

PE utilise l'API ennemi et la méthode d'accès ne fait pas partie de cette API.


J'ai mis à jour mon hypothèse sur ce qui se passe dans l'exemple.


3 Réponses :


0
votes

fonctionne avec le même nom, mais des signatures différentes sont des fonctions essentiellement différentes.

En déclarant vide virtuel Décrivez (INT DUMMY) Dans votre classe DRAGON , vous avez déclaré une nouvelle fonction virtuelle, qui ne remplace pas l'original ( vide virtuel décris () dans ennemi ). Vous pouvez seulement remplacer les fonctions virtuelles avec la même signature.

Vous ne pouvez pas appeler décrivec (1) sur un pointeur sur ennemi car C ++ appelle fonction en fonction du type d'heure de compilation de l'instance (bien qu'un tel appel puisse être expédié de manière dynamique à Appelez la méthode de remplacement réelle).


5 commentaires

Mais pourquoi la fonction est-elle expédiée à l'heure de la compilation mais pas d'exécution? À partir de ma compréhension, si la fonction est expédiée pendant l'exécution, le programme devrait pouvoir trouver un décrire dans SRONGRAGE 'S Tablevis, bien qu'il soit sans rapport avec le Décrivez dans ennemi . Est-ce vrai?


@LUCIDASEASE La fonction est présente dans la table de table, mais on ne peut pas accéder à partir d'un pointeur sur ennemi , car ennemi n'a pas cette fonction de membre (il en a un avec le même nom , mais pas la même fonction de membre).


@Lucidase également, réfléchissez du point de vue du compilateur. Si vous avez un autre type, dites, soldat qui dérive également de ennemi , qui a la méthode décrire , mais avec la signature décrire ( ) (sans int ). Vous donnez au compilateur un pointeur à ennemi et appelez décrivez (1) dessus. Comment le compilateur voudrait-il savoir au compilateur s'il a dragon type ou soldat type? Les méthodes virtuelles sont un bon moyen de créer une interface Solid pour appeler des appels de fonctions, pour ne pas improviser à différents paramètres.


Mais lorsque la fonction est correctement annulée, le programme peut accéder à DRAGON S à partir d'un "code> ennemi * pointeur. Et d'après ce que j'ai appris, il semble que la table / expédition dynamique est exactement les mécanismes pour faire face aux situations où le type n'est pas connu pendant la compilation.


@Lucidase Ce que vous suggère est théoriquement possible (preuve: il y a dynamic_cast en C ++), mais ils ne sont tout simplement pas autorisés dans votre contexte.



0
votes

en C ++, fonctions qui ont le même nom, mais différents paramètres sont des fonctions totalement indépendantes, qui n'ont rien à voir avec l'autre . Le fait qu'ils aient le même nom est complètement immatériel.

C'est exactement la même chose que si vous appeliez la fonction dans la classe de base "Apple", et celle de la classe dérivée "banane". Comme il n'existe aucune fonction "banane" dans la classe de base, vous ne pouvez pas évidemment l'appeler dans la classe de base. La fonction de banane dans la classe dérivée ne remplace évidemment pas la fonction de la classe de base.

Je sais aussi que la redéfinition d'un nom de la fonction dans la classe dérivée va cacher toutes les fonctions du même nom dans la classe de base.

c'est incorrect. Il ne la cache que s'il a le même nom, mais également des paramètres identiques (et des qualificatifs, s'il n'y en a pas ou n'est pas).


0 commentaires

0
votes

En fait, vous avez:

class Dragon : public Enemy {
public:
  using Enemy::describe; // Unhide Enemy::describe()

  virtual void describe(int dummy) { std::cout << "Dragon"; }
};
  • Pointants / Références sur Enemy CODE> VOIR UNIQUEMENT Voir NOID ENNEMY :: Décrivez () CODE> P> LI>

  • Pointants / Références sur Dragon Code> seulement Voir Void Dragon :: Décrivez (INT) CODE> (mais pourrait avoir explicitement accès à ennemi annulé :: décrit () code>). p> li> ul>

    L'envoi virtuel est effectué avec type d'exécution. p>

    SO P>

    Dragon foo;
    Enemy* pe = &foo;
    
    foo.describe();         // KO: Enemy::describe() not visible (1)
    foo.Enemy::describe();  // OK: Enemy::describe()
    foo.describe(1);        // OK: Dragon::describe(int)
    
    pe->describe();         // OK: Enemy::describe()
    pe->describe(1);        // KO: No Enemy::describe(int)
    pe->Dragon::describe(1);// KO: Dragon is not a base class of Enemy
    


2 commentaires

Mais pourquoi pe-> décrivez est la liaison statique (envoi) au lieu de la liaison dynamique? J'ai supposé que pendant l'exécution, lorsque le programme tente d'envoyer une méthode virtuelle, il examinera la table de fonction virtuelle de l'objet. Dans ce cas, il est Dragon S Tablevisible.


Void (C :: *) Décrivez () et VOID (C :: *) Décrivez (int) sont 2 signatures différentes. Dragon Ttable aurait 2 entrées, une pour ennemi :: décrivant (() (ce qu'elle ne remplace pas, alors aurait la même valeur qu'un ennemi ), un pour dragon :: décrit (int) .