4

有人可以我摆脱痛苦吗?我试图弄清楚为什么派生 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

我知道当我发现问题所在时我会踢自己,但如果有人能轻轻地让我失望,那将不胜感激。

4

5 回答 5

9

那是因为您还没有使您的 operator== 虚拟,因此在运行时不考虑实际类型。

不幸的是,仅仅使 operator== 虚拟并不能解决您的问题。原因是当您通过将参数的类型从基更改为派生来更改函数签名时,您实际上是在创建一个新函数。听起来您想研究双重调度来解决您的问题。

于 2010-01-26T23:11:06.420 回答
4

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

第一个解决方案。我建议在循环中添加一些额外的类型逻辑,这样你就知道什么时候有 aBase和什么时候有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_;
}
于 2010-01-27T02:03:24.543 回答
2

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

于 2010-01-26T23:14:36.413 回答
1

当成员函数是虚拟的时,虚拟表在运行时用于多态地调用指针实际指向的类型的函数(在这种情况下,您的类 Derived)。当函数不是虚函数时,不会进行虚表查找,并且调用给定类型的函数(在这种情况下,是您的类 Base)。

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

于 2010-01-26T23:16:37.930 回答
0

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

于 2010-01-26T23:16:23.017 回答