0

I want to implement a function to compare derived class. But I found that I have to cast the base class object to derived class object.

Is there a way to avoid using cast here or another better design to implement the compare function ?

class A {
    public :
        virtual bool equal(const A & obj) const = 0;
};

class AA : public A{
    public:
        AA (int i) : m_i(i) {}
        virtual bool equal(const A & obj) const
        {
            return m_i == dynamic_cast<const AA&>(obj).m_i;
        }

    private:
        int m_i;
};

int main () {
    AA aa1(10), aa2(9);
    A &a1 = aa1, &a2 = aa2;

    a1.equal(a2);

    return 0;
}
Cong Ma
  • 1,241
  • 1
  • 11
  • 20
  • 2
    Be aware that `dynamic_cast` to a reference type may throw an exception. Might be better to cast to a pointer instead, you probably just want to return `false` if the pointer comes back NULL. – Mark Ransom Sep 26 '14 at 20:53
  • Maybe using a getter that is virtual in the base class for the member variable `m_i`? – abiessu Sep 26 '14 at 20:53
  • Read about double dispatch and about classical solution for it - pattern visitor. – Ilya Sep 26 '14 at 20:56
  • Your code will throw an exception with your dynamic cast if obj is not an AA. It'd work if you used a pointer instead. How to avoid it would be based on how you are using the equals and your overall polymorphic design. – IdeaHat Sep 26 '14 at 20:57
  • As I see it it's ungood to have the equality operation consider more information (in dynamic type) than the programmer is aware of (in static type). However, while this is unnatural in C++ it has, as I understand it, been thoroughly investigated in Java. I would look there for answers, and possibly, switch from C++ to Java if I wanted the kind of behavior, pretty dynamic typing, that is involved here. – Cheers and hth. - Alf Sep 26 '14 at 20:58
  • You really don't want to put the `operator==` in the base class. If I have a base class, `Fruit`, then the operator will allow me to compare strawberries to pineapples or base portion of strawberries to the base portion of mangoes. – Thomas Matthews Sep 26 '14 at 20:58
  • I just put my own answer into the question marked as duplicate: http://stackoverflow.com/questions/1691007/whats-the-right-way-to-overload-operator-for-a-class-hierarchy – Mark Ransom Sep 27 '14 at 01:51

1 Answers1

0

I've been bitten by this request many times in my own code. I'll present a simplified example:

struct Fruit
{
  virtual bool is_equal(Fruit const & f) const = 0; // Compare two fruits
  // Some dangerous actions:
  bool operator==Fruit const & f)
  {
    return is_equal(f); // Dispatch!
  }
};

struct Strawberry : public Fruit
{
  bool is_equal(Fruit const & f)
  {
     bool equal = false;
     // The f could be any fruit, such as tomatoes or pineapples or bananas.
     // Need to perform a dynamic_cast to verify that the f is a strawberry.
     Strawberry const & s = dynamic_cast<Strawberry const &>(f);
     // perform strawberry comparison;
     return equal;
  };
}

struct Banana : public Fruit
{
  bool is_equal(Fruit const & f)
  {
     bool equal = false;
     // The f could be any fruit, such as tomatoes or pineapples or strawberries.
     // Need to perform a dynamic_cast to verify that the f is a banana.
     Banana const & b = dynamic_cast<Banana const &>(f);
     // perform banana comparison;
     return equal;
  };
}

bool Compare_Fruits(Fruit const * pf1, Fruit const * pf2)
{
  if (*pf1 == *pf2)
  {
    cout << "Fruits are equal\n";
    return true;
  }
  cout << "Fruits are different\n";
  return false;
}

int main(void)
{
  // Fun with fruit.
  Fruit * p_fruit_1 = new Strawberry;
  Fruit * p_fruit_2 = new Banana;
  Fruit * p_fruit_3 = new Strawberry;

  // Is this valid, comparing two different fruits, when
  // we just want to compare two strawberries?
  if (Compare_Fruits(p_fruit_1, p_fruit_3)) // OK, both are strawberries
  {
     // ...
  }
  if (Compare_Fruits(p_fruit_1, p_fruit_2)) // Not OK, two different fruits.
  {
     // ...
  }
  return 0;
}  

The point here is that if you are implementing the equal operation in the base class so that descendents can compare instances, you have made a poor decision. Be aware that I can pass a pointer to an instance of one descendent (Strawberry) to the comparison method of another descendent (Banana), because the equality function is based on pointers to the base class. Unfortunately, there is no way to tell how many or all of the descendents that are derived from the base class.

For programming safety, I highly recommend against placing virtual comparison operators in the base class. The base class should have comparison operators, declared protected, that only compare the base data members. This method would be called by the descendents.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • How then, if you have a pair of objects known only by base class pointer or reference, would you know if they're equal? – Mark Ransom Sep 26 '14 at 21:33
  • If two objects, known by base class pointer or reference, can only be compared using the data of the base class. You can only compare for content equality if and only if the two objects are instances of the same class. Again, what is being compare when the base class is `Life_Form` and one instance is a tree and the other is a spider? It doesn't make sense. In my project, where `Field` is a base class, I have a `Record` which is a container of `Field`. How can I be sure that I am not comparing a `Field::Text` to a `Field::Double`? – Thomas Matthews Sep 27 '14 at 00:14
  • Using `dynamic_cast` with a virtual comparison function as shown in the question you can easily put a comparison function on the base class - you just can't *implement* it there. – Mark Ransom Sep 27 '14 at 00:41