Question

    

Cette question a déjà une réponse ici:

         

Étant donné l'exemple suivant, pourquoi dois-je utiliser explicitement l'instruction b- & A; Do :: Quelque chose () plutôt que simplement b- > DoSomething () ?

La résolution de surcharge du compilateur ne devrait-elle pas déterminer la méthode dont je parle?

J'utilise Microsoft VS 2005. (Remarque: l'utilisation de virtual n'aide pas dans ce cas.)

class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}
Était-ce utile?

La solution

Les deux & # 8220; surcharges & # 8221; ne sont pas dans la même portée. Par défaut, le compilateur considère uniquement la plus petite portée de nom possible jusqu'à ce qu'il trouve une correspondance de nom. La correspondance d'arguments est effectuée après . Dans votre cas, cela signifie que le compilateur voit B :: DoSomething . Il essaie ensuite de faire correspondre la liste des arguments, ce qui échoue.

Une solution serait de réduire la surcharge de A dans le champ d'application de B :

class B : public A {
public:
    using A::DoSomething;
    // …
}

Autres conseils

La résolution de surcharge est l’une des parties les plus laides de C ++

En gros, le compilateur trouve un nom correspondant à "DoSomething (int)". dans le champ d'application de B, voit les paramètres ne correspondent pas et s'arrête avec une erreur.

Il peut être surmonté en utilisant le A :: DoSomething en classe B

class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;} 
};   


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}

Non, ce comportement est présent pour vous éviter de vous faire attraper par erreur en héritant de classes de base distantes.

Pour contourner ce problème, vous devez indiquer au compilateur la méthode à appeler en plaçant un objet A :: DoSomething dans la classe B.

Voir cet article pour un aperçu rapide et facile de ce comportement.

La présence d'une méthode dans une classe dérivée masque toutes les méthodes du même nom (quels que soient les paramètres) dans les classes de base. Ceci est fait pour éviter des problèmes comme celui-ci:

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

plus tard, quelqu'un change de classe A:

class A
{
    void DoSomething(int ) {...}
}

maintenant tout à coup:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

En d'autres termes, si cela ne fonctionne pas comme cela, un changement non lié à une classe que vous ne maîtrisez pas (A) peut affecter le fonctionnement de votre code en mode silencieux.

Cela a quelque chose à voir avec le fonctionnement de la résolution de noms. Fondamentalement, nous trouvons d'abord la portée d'où provient le nom, puis nous collectons toutes les surcharges pour ce nom dans cette portée. Toutefois, dans votre cas, la portée est celle de la classe B, et celle de la classe B, B :: DoSomething cache A :: DOSomething:

3.3.7 Masquage du nom [basic.scope.hiding]

... [snip] ...

3 Dans une définition de fonction membre, la déclaration d'un nom local cache   la déclaration d'un membre de la classe portant le même nom; voir    basic.scope.class . La déclaration d'un membre dans une classe dérivée   ( class.derived ) masque la déclaration d'un membre d'une classe de base de   Le même nom; voir class.member.lookup .

En raison du masquage de nom, A :: DoSomething n’est même pas pris en compte pour la résolution des surcharges

Lorsque vous définissez une fonction dans une classe dérivée, toutes les fonctions portant ce nom sont masquées dans la classe de base. Si la fonction de classe de base est virtuelle et possède une signature compatible, la fonction de classe dérivée remplace également la fonction de classe de base. Cependant, cela n’affecte pas la visibilité.

Vous pouvez rendre la fonction de classe de base visible avec une déclaration using:

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  

Cela ne surcharge pas! C'est caché!

Lors de la recherche dans la hiérarchie de l'héritage de la fonction à utiliser, C ++ utilise le nom sans arguments, une fois la définition trouvée arrêtée, puis examine les arguments. Dans l'exemple donné, il s'arrête en classe B. Pour pouvoir faire ce que vous voulez, la classe B doit être définie comme suit:

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 

La fonction est masquée par la fonction portant le même nom dans la sous-classe (mais avec une signature différente). Vous pouvez l'afficher à l'aide de l'instruction using, comme dans l'utilisation de A :: DoSomething ();

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top