L’opérateur == dans la classe dérivée n’est jamais appelé

Quelqu’un peut-il s’il vous plaît me sortir de ma misère avec ça? J’essaie de comprendre pourquoi un opérateur dérivé == n’est jamais appelé en boucle. Pour simplifier l’exemple, voici ma classe de base et dérivée:

class Base { // ... snipped bool operator==( const Base& other ) const { return name_ == other.name_; } }; class Derived : public Base { // ... snipped bool operator==( const Derived& other ) const { return ( static_cast( *this ) == static_cast( other ) ? age_ == other.age_ : false ); }; 

Maintenant, quand j’instancie et compare comme ça …

 Derived p1("Sarah", 42); Derived p2("Sarah", 42); bool z = ( p1 == p2 ); 

… tout est bon. Ici, l’opérateur == de Derived est appelé, mais lorsque je boucle sur une liste, je compare les éléments d’une liste de pointeurs aux objects de base …

 list coll; coll.push_back( new Base("fred") ); coll.push_back( new Derived("sarah", 42) ); // ... snipped // Get two items from the list. Base& obj1 = **itr; Base& obj2 = **itr2; cout << obj1.asString() << " " << ( ( obj1 == obj2 ) ? "==" : "!=" ) << " " << obj2.asString() << endl; 

Ici, asSsortingng() (qui est virtuel et non présenté ici pour des raisons de brièveté) fonctionne bien, mais obj1 == obj2 appelle toujours l’ operator== Base operator== même si les deux objects sont Derived .

Je sais que je vais me donner des coups quand je découvrirai ce qui ne va pas, mais si quelqu’un pouvait me laisser tomber doucement, ce serait très apprécié.

Il y a deux façons de résoudre ce problème.

Première solution. Je suggérerais d’append une logique de type supplémentaire à la boucle afin que vous sachiez quand vous avez une Base et quand vous avez une Derived . Si vous ne travaillez vraiment qu’avec des objects Derived , utilisez

 list coll; 

sinon, mettez un dynamic_cast quelque part.

Deuxième solution. Mettez le même genre de logique dans votre operator== . Commencez par le rendre virtuel afin que le type de l’opérande gauche soit déterminé lors de l’exécution. Puis vérifiez manuellement le type de l’opérande de droite.

 virtual bool operator==( const Base& other ) const { if ( ! Base::operator==( other ) ) return false; Derived *other_derived = dynamic_cast< Derived * >( &other ); if ( ! other_derived ) return false; return age_ == other_derived->age_; } 

mais étant donné que des objects de types différents ne seront probablement pas égaux, ce que vous voulez est probablement

 virtual bool operator==( const Base& other ) const { Derived *other_derived = dynamic_cast< Derived * >( &other ); return other_derived && Base::operator==( other ) && age_ == other_derived->age_; } 

C’est parce que vous n’avez pas rendu votre opérateur == virtuel, de sorte que le type réel n’est pas pris en compte au moment de l’exécution.

Malheureusement, le simple fait de rendre l’opérateur == virtuel ne résoudra pas votre problème. La raison en est que lorsque vous modifiez la signature de la fonction en modifiant le type de l’argument de base en dérivé, vous créez en fait une nouvelle fonction. Il semble que vous souhaitiez examiner la double dépêche pour résoudre votre problème.

Vous devez rendre operator== virtual et vous devez vous assurer que les deux méthodes ont la même signature. c’est-à-dire qu’ils devront probablement prendre la Base . Vous pourriez avoir un operator== surchargé operator== dans votre classe dérivée qui serait capable de gérer des objects dérivés.

Lorsqu’une fonction membre est virtuelle, la table virtuelle est utilisée au moment de l’exécution pour appeler de manière polymorphe la fonction sur le type pointé par le pointeur (dans ce cas, votre classe Derived). Lorsqu’une fonction n’est pas virtuelle, aucune recherche de table virtuelle n’est effectuée et la fonction du type donné est appelée (dans ce cas, votre classe Base).

Ici, vos fonctions operator = () n’étant pas virtuelles, le type du pointeur est utilisé plutôt que le type indiqué par le pointeur.

Pour que les classes dérivées utilisent leur propre implémentation d’opérateur, l’opérateur doit être virtuel dans la classe de base, sinon l’implémentation de classes de base sera utilisée.