13

重複の可能性:
クラス階層のoperator ==をオーバーロードする正しい方法は何ですか?

C ++では、派生クラスはどのようにして基本クラスの同等性テストを意味のある方法でオーバーライドできますか?

たとえば、基本クラスAがあるとします。クラスBとCはAから派生します。2つのAオブジェクトへの2つのポインターが与えられた場合、それらが等しいかどうかをテストできますか(サブクラスデータを含む)?

class A {
    public: int data;
};

class B : public A {
    public: float more_data; bool something_else;
};

class C : public A {
    public: double more_data;
};


    A* one = new B;
    A* two = new B;
    A* three = new C;

    //How can I test if one, two, or three are equal
    //including any derived class data?

それを行うためのクリーンな方法はありますか?私の最善の策は何ですか?

ありがとう!

4

5 に答える 5

15

public-non-virtual / non-public-virtualイディオムとその利点の簡潔な説明を読んだことを覚えていますが、どこではありません。 このウィキブックスには大丈夫な説明があります。

これをop==に適用する方法は次のとおりです。

struct A {
  virtual ~A() {}

  int a;

  friend
  bool operator==(A const& lhs, A const& rhs) {
    return lhs.equal_to(rhs);
  }
  // http://en.wikipedia.org/wiki/Barton-Nackman_trick
  // used in a simplified form here

protected:
  virtual bool equal_to(A const& other) const {
    return a == other.a;
  }
};

struct B : A {
  int b;

protected:
  virtual bool equal_to(A const& other) const {
    if (B const* p = dynamic_cast<B const*>(&other)) {
      return A::equal_to(other) && b == p->b;
    }
    else {
      return false;
    }
  }
};

struct C : A {
  int c;

protected:
  virtual bool equal_to(A const& other) const {
    if (C const* p = dynamic_cast<C const*>(&other)) {
      return A::equal_to(other) && c == p->c;
    }
    else {
      return false;
    }
  }
};
于 2009-11-19T17:45:27.327 に答える
2

異なる派生クラスは同等のオブジェクトを作成できますか?

その場合:ダブルディスパッチはオプションです:基本クラスでオーバーロードする必要があるため、依存関係があります

そうでない場合:タイプIDをチェックし、異なる場合はfalseを返すための解決策がoperator ==()にあります。それ以外の場合は、派生クラスがstatic_castを実行して比較できるプライベートequal()関数を呼び出します。

bool base::operator==(const base& other) const
{
  if (typeid(*this) != typeid(other)) return false;
  return equal(other);
}

bool derived::equal(const base& other) const
{
  derived& derOther = static_cast<derived&>(other);
  // compare derOther with *this
  return true;  // there is nothing to compare
}

これにより、すべての派生クラスでの型チェックが回避されます

于 2009-11-19T17:54:27.780 に答える
1

これを行う1つの方法はvirtual operator==、基本クラスオブジェクトをパラメーターとして使用して、さまざまな派生オブジェクトで適切に機能するようにすることです。ただし、すべての派生オブジェクトに強制的に実装させるには、この関数を純粋仮想にする必要があります。したがって、基本クラスをインスタンス化することはできません。例えば:

class A
{
public:
    virtual ~A(){}

    //A virtual operator for comparison
    virtual bool operator==(const A& a) = 0;

protected:
    bool compareBase(const A& a);

private:
    int m_data;
};

bool A::compareBase(const A &a)
{
    return m_data == a.m_data;
}

class B1 : public A
{
public:

    //Override the base class
    bool operator==(const A& a);

private:
    bool compare(const B1* pB)
    {
        if(compareBase(*pB))
        {
            //Code for compare
            return true;
        }

        return false;
    }
};

bool B1::operator ==(const A &a)
{
    //Make sure that the passed type is same
    const B1* pB = dynamic_cast<const B1*>(&a);
    if(pB )
    {
        return compare(pB);
    }

    return false;
}
//Similarly implement for B2
于 2009-11-19T17:53:23.147 に答える
0

タイプAとタイプB、またはBとCなどの比較を気にしない場合は、クラスごとにオーバーロードされた等式演算子を実装するだけです。

class A {
    public: int data;

    bool operator==(const A& rhs) {
        return (data == rhs.data);
    }
};
class B : public A {
    public: float more_data; bool something_else;

    bool operator==(const B& rhs) {
        return (A::operator==( data ) &&
                more_data == rhs.more_data &&
                something_else == rhs.something_else);
    }
};

ただし、BまたはCから新しいクラスDを派生させると、問題が発生するため、これは危険です。

それ以外の場合は、実際に正しく実行するために、多くのdynamic_cast<>-ingを使用していくつかのコンパレータを実装する必要があります。または、各オブジェクトのハッシュコードを作成し、それを活用する関数を実装することもできます。

class A {
    public: int data;

    virtual long getHashCode() const {
        // compute something here for object type A
    }

    // virtual here just in case you need to overload it in B or C
    virtual bool equals( const A& obj ) const {
        return (typeid(*this) == typeid(obj) &&
                getHashCode() == obj->getHashCode());
    }
};

class B : public A {
    public: float more_data; bool something_else;

    virtual long getHashCode() const {
        // compute something here for object type B
    }
};

class C : public A {
    public: double more_data;

    virtual long getHashCode() const {
        // compute something here for object type C
    }
};

オブジェクトの型を何らかの方法でハッシュコードに組み込む場合(上記には示されていません)、上記のばかげたtypeid()の比較を省くこともできます。

于 2009-11-19T18:02:40.553 に答える
0

サブクラスを参照する基本クラスを気にしない場合は、ダブルディスパッチします。

#include <iostream>

class B;
class C;

class A
{
public:
    int data;

    virtual bool equals (const A* rhs) const
    {
        std::cout << " A==A ";
        return data == rhs->data;
    }

    virtual bool equals (const B* rhs) const {return false;}
    virtual bool equals (const C* rhs) const {return false;}
};

class B : public A
{
public:
    float some_data;

    virtual bool equals (const A* rhs) const
    {
        return rhs->equals (this);
    }

    virtual bool equals (const B* rhs) const
    {
        std::cout << " B==B ";
        return A::equals (static_cast<const A*> (rhs)) && some_data == rhs->some_data;
    }
};

class C : public A
{
public:
    double more_data;

    virtual bool equals (const A* rhs) const
    {
        return rhs->equals (this);
    }

    virtual bool equals (const C* rhs) const
    {
        std::cout << " C==C ";
        return A::equals (static_cast<const A*> (rhs)) && more_data == rhs->more_data;
    }
};

bool operator== (const A& lhs, const A& rhs)
{
    return lhs.equals (&rhs);
}

int main (int argc, char* argv[])
{

    A* one = new B;
    A* two = new B;
    A* three = new C;

    std::cout << (*one == *one) << std::endl;
    std::cout << (*one == *two) << std::endl;
    std::cout << (*one == *three) << std::endl;
    std::cout << (*three == *three) << std::endl;

    return 0;
}

dynamic_castsを必要とせずにそれを行います。

于 2009-11-19T18:11:27.087 に答える