Equality Test for Derived Classes in C++ [duplicate]

泄露秘密 提交于 2019-12-18 10:45:01

问题


Possible Duplicate:
What’s the right way to overload operator== for a class hierarchy?

In C++, how can derived classes override the base class equality test in a meaningful way?

For example, say I have a base class A. Classes B and C derive from A. Now given two pointers to two A objects, can I test if they are equal (including any subclass data)?

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?

Is there a clean way of doing it? What's my best bet?

Thanks!


回答1:


I remember reading a succinct description of the public-non-virtual/non-public-virtual idiom and its advantages, but not where. This wikibook has an okay description.

Here is how you apply it to 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;
    }
  }
};



回答2:


Can different derived classes make equal objects?

If so: double dispatch is an option: it does need overloading in the base class, so you will have dependencies

If not: a solution is in the operator==() to check the typeid and return false if they're different. Otherwise call a private equal() function in which the derived class can do a static_cast and compare.

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
}

This avoids type-checks in all derived classes




回答3:


One way of doing this is to use the virtual operator== which takes the base class object as the parameter so that it works properly with different derived objects. However, you need to make this function pure virtual so as to force all the derived objects to implement it. So you will not be able instantiate the base class. For example:

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



回答4:


If you don't care about comparisons of type A to type B, or B to C, etc. then you can simply implement an overloaded equality operator for each class:

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);
    }
};

That's dangerous though, because if you derive a new class D from B or C, you're going to have problems.

Otherwise you need to implement some comparators with a lot of dynamic_cast<>-ing to really do it right. Alternatively you could implement a function to create a hash code for each object and leverage that, e.g.

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
    }
};

If you incorporate the object's type into the hash code in some fashion (not shown above) then you can also dispense with the silly typeid() comparisons above.




回答5:


If you don't mind the base class referring to the sub-classes then double-dispatch:

#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;
}

Does it without requiring dynamic_casts.



来源:https://stackoverflow.com/questions/1765122/equality-test-for-derived-classes-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!