Оператор == в производном классе никогда не вызывается - PullRequest
4 голосов
/ 27 января 2010

Может ли кто-нибудь пожалуйста избавить меня от моих страданий этим? Я пытаюсь выяснить, почему производный оператор == никогда не вызывается в цикле. Чтобы упростить пример, вот мой класс Base и Derived:

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<const Base&>( *this ) ==
             static_cast<const Base&>( other ) ? age_ == other.age_ :
                                                 false );
};

Теперь, когда я создаю экземпляр и сравниваю вот так ...

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

... все хорошо. Здесь вызывается оператор == из Derived, но когда я зацикливаюсь на списке, сравнивая элементы в списке указателей с объектами Base ...

list<Base*> 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;

Здесь asString() (который является виртуальным и не показан здесь для краткости) работает нормально, но obj1 == obj2 всегда вызывает Base operator==, даже если два объекта Derived.

Я знаю, что собираюсь надрать себя, когда узнаю, что не так, но если бы кто-то мог мягко подвести меня, это было бы очень признательно.

Ответы [ 5 ]

9 голосов
/ 27 января 2010

Это потому, что вы не сделали свой оператор == виртуальным, поэтому фактический тип не учитывается во время выполнения.

К сожалению, просто сделать оператор == виртуальным не решит вашу проблему. Причина в том, что когда вы меняете сигнатуру функции путем изменения типа аргумента с базового на производный, вы фактически создаете новую функцию. Похоже, вы хотите заглянуть в двойную диспетчеризацию , чтобы решить вашу проблему.

4 голосов
/ 27 января 2010

Есть два способа исправить это.

Первое решение. Я бы предложил добавить в цикл некоторую дополнительную логику типов, чтобы вы знали, когда у вас есть Base, а когда у вас есть Derived. Если вы действительно имеете дело только с Derived объектами, используйте

list<Derived*> coll;

в противном случае положите куда-нибудь dynamic_cast.

Второе решение. Поместите такую ​​же логику в ваш operator==. Сначала сделайте его виртуальным, чтобы тип левого операнда определялся во время выполнения. Затем вручную проверьте тип правого операнда.

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_;
}

но с учетом того, что объекты разных типов, вероятно, не будут равны, вероятно, то, что вы хотите, это

virtual bool operator==( const Base& other ) const {
  Derived *other_derived = dynamic_cast< Derived * >( &other );
  return other_derived
   && Base::operator==( other )
   && age_ == other_derived->age_;
}
2 голосов
/ 27 января 2010

Вам нужно сделать operator== virtual, и вам нужно убедиться, что оба метода имеют одинаковую подпись. то есть им, вероятно, нужно будет принять Base. В вашем производном классе может быть перегружен operator==, который сможет обрабатывать производные объекты.

1 голос
/ 27 января 2010

Когда функция-член является виртуальной, виртуальная таблица используется во время выполнения для полиморфного вызова функции того типа, на который фактически указывает указатель (в данном случае, ваш класс Derived). Когда функция не является виртуальной, поиск виртуальной таблицы не выполняется, и вызывается функция данного типа (в данном случае, ваш класс Base).

Здесь функции operator = () не являются виртуальными, поэтому вместо указателя указывается тип указателя.

0 голосов
/ 27 января 2010

Чтобы производные классы могли использовать собственную реализацию оператора, оператор должен быть виртуальным в базовом классе, в противном случае вместо него будет использоваться реализация базовых классов.

...