派生类中的运算符==永远不会被调用

时间:2010-01-26 23:07:17

标签: c++ operators polymorphism virtual equals-operator

有人能让我摆脱苦难吗?我试图弄清楚为什么派生运算符==永远不会在循环中被调用。为了简化示例,这是我的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的operator ==,但是当我遍历列表时,将指针列表中的项目与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 个答案:

答案 0 :(得分:9)

那是因为你没有让你的算子==虚拟,所以在运行时不考虑实际的类型。

不幸的是,只是让运营商==虚拟无法解决您的问题。原因是当您通过将参数的类型从base更改为derived来更改函数签名时,实际上是在创建一个新函数。听起来你想要调查double-dispatch来解决你的问题。

答案 1 :(得分:4)

有两种方法可以解决这个问题。

第一个解决方案。我建议在循环中添加一些额外的类型逻辑,以便知道何时有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 :(得分:2)

您需要制作operator== virtual,并且需要确保它们两种方法都具有相同的签名。即他们可能需要同时采取Base。您可以在派生类中重载operator==,以便能够处理派生对象。

答案 3 :(得分:1)

当成员函数是虚拟的时,虚拟表在运行时用于多态调用指针实际指向的类型的函数(在本例中为您的类Derived)。当一个函数不是虚函数时,不会进行虚拟表查找,并且调用给定类型的函数(在本例中为您的类Base)。

这里,您的operator =()函数不是虚函数,因此使用指针的类型而不是指针指向的类型。

答案 4 :(得分:0)

对于派生类使用自己的运算符实现,运算符必须在基类中是虚拟的,否则将使用基类实现。