1. ホーム
  2. c++

[解決済み] クラス階層に対してoperator==をオーバーロードする正しい方法は何ですか?

2022-03-06 13:18:01

質問

次のようなクラス階層があるとします。

class A
{
    int foo;
    virtual ~A() = 0;
};

A::~A() {}

class B : public A
{
    int bar;
};

class C : public A
{
    int baz;
};

をオーバーロードする正しい方法は何ですか? operator== は、これらのクラスのために? もし、これらをすべてフリー関数にすると、BとCはキャストしないとAのバージョンを活用できない。 また、Aへの参照だけを持っている人が深い比較をすることもできなくなります。

bool B::operator==(const A& rhs) const
{
    const B* ptr = dynamic_cast<const B*>(&rhs);        
    if (ptr != 0) {
        return (bar == ptr->bar) && (A::operator==(*this, rhs));
    }
    else {
        return false;
    }
}

今回もやはりキャストしなければなりません(間違っているような気がします)。 何か好ましい方法はないでしょうか?

更新してください。

今のところ回答は2つしかありませんが、正しい方法は代入演算子と類似しているようです。

  • リーフ以外のクラスを抽象化する
  • 非リーフクラスでの非仮想の保護
  • リーフクラスにおけるパブリック非仮想

基本関数は保護されており、リーフクラスは親のバージョンを利用してデータのその部分を比較できるため、異なる型の2つのオブジェクトをユーザーが比較しようとすると、コンパイルされないでしょう。

どうやって解決するの?

このような階層構造では、Scott MeyerのEffective C++のアドバイスに従って、具体的な基底クラスを持たないようにするのが間違いないでしょう。いずれにせよ、あなたはこれを実行しているように見えます。

私なら operator== を、具体的なリーフ・ノード・クラスのタイプに対してのみ、自由な関数として、おそらくは友人として、提供します。

もしベースクラスがデータメンバーを持たなければならないのであれば、ベースクラス内に(おそらく保護された)非仮想ヘルパー関数を提供します ( isEqual というように)、派生クラスの operator== を使用することができます。

bool operator==(const B& lhs, const B& rhs)
{
    return lhs.isEqual( rhs ) && lhs.bar == rhs.bar;
}

を持たないようにすることで operator== 抽象的な基底クラスで動作する比較関数を保護したままにしておけば、型が異なる2つのオブジェクトの基底部分のみを比較するようなフォールバックをクライアントコードで誤って行うことはありません。

仮想比較関数を実装する際に dynamic_cast しかし、もしその必要性が証明されているのであれば、私はおそらくベースクラスの純粋な仮想関数にするでしょう ( ではなく operator== ) を使って、具体的な派生クラスで次のようにオーバーライドされました。 operator== を派生クラスに追加しました。

bool B::pubIsEqual( const A& rhs ) const
{
    const B* b = dynamic_cast< const B* >( &rhs );
    return b != NULL && *this == *b;
}