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()(これは仮想であり、簡潔にするためにここには示していません) は正常に動作しますが、2 つのオブジェクトが であってもobj1 == obj2 常に を呼び出します。Base operator==Derived

何が悪いのか分かったら自分を蹴ってしまうことはわかっていますが、誰かが私を優しく失望させてくれるなら、それは大歓迎です.

4

5 に答える 5

9

これは、 operator== virtual を作成していないため、実行時に実際の型が考慮されないためです。

残念ながら、 operator== virtual にするだけでは問題は解決しません。その理由は、引数の型を基底型から派生型に変更して関数シグネチャを変更すると、実際には新しい関数を作成することになるからです。問題を解決するために二重発送を検討したいようです。

于 2010-01-26T23:11:06.420 に答える
4

これを修正するには 2 つの方法があります。

最初の解決策。ループに追加のタイプ ロジックを追加することをお勧めしBaseますDerivedDerived本当にオブジェクトだけを扱っている場合は、

list<Derived*> coll;

dynamic_castそれ以外の場合は、どこかに置きます。

2番目の解決策。同じ種類のロジックを に入れます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あり、両方のメソッドが同じ署名を持っていることを確認する必要があります。つまり、おそらく両方を取る必要がありますBaseoperator==派生オブジェクトを処理できる派生クラスにオーバーロードすることができます。

于 2010-01-26T23:14:36.413 に答える
1

メンバー関数が仮想の場合、実行時に仮想テーブルを使用して、ポインターが実際に指す型 (この場合は派生クラス) で関数を多態的に呼び出します。関数が仮想でない場合、仮想テーブルのルックアップは行われず、指定された型の関数が呼び出されます (この場合はクラス Base)。

ここで、 operator=() 関数は仮想ではないため、ポインターが指す型ではなく、ポインターの型が使用されます。

于 2010-01-26T23:16:37.930 に答える
0

派生クラスが演算子の独自の実装を使用するには、演算子が基本クラスで仮想である必要があります。そうでない場合は、代わりに基本クラスの実装が使用されます。

于 2010-01-26T23:16:23.017 に答える