Comparing polymorphic base types in C ++ without RTTI

I have some pointers to the base type of Shape. I want to compare these objects using the == operator. The == operator must explicitly return false if the objects are of a different derived type. If they have the same derived type, then the members of the derived type should be compared.

I read that using C ++ RTTI is bad practice and should only be used in rare and significant circumstances. As far as I can see, this problem cannot be solved without using RTTI. Each overloaded operator == needs to check the typeid, and if they are the same, do dynamic_cast and compare the elements. This seems like a common need. Is there any idiom for this problem?

#include <iostream>
using namespace std;

class Shape {
  public:
    Shape() {}
    virtual ~Shape() {}
    virtual void draw() = 0;

    virtual bool operator == (const Shape &Other) const = 0;
};

class Circle : public Shape {
  public:
    Circle() {}
    virtual ~Circle() {}
    virtual void draw() { cout << "Circle"; }

    virtual bool operator == (const Shape &Other) const {
      // If Shape is a Circle then compare radii
    }

  private:
    int radius;
};

class Rectangle : public Shape {
  public:
    Rectangle() {}
    virtual ~Rectangle() {}
    virtual void draw() { cout << "Rectangle"; }

    virtual bool operator == (const Shape &Other) const {
      // If Shape is a Rectangle then compare width and height
    }

  private:
    int width;
    int height;
};

int main() {
  Circle circle;
  Rectangle rectangle;

  Shape *Shape1 = &circle;
  Shape *Shape2 = &rectangle;

  (*Shape1) == (*Shape2); // Calls Circle ==
  (*Shape2) == (*Shape1); // Calls Rectangle ==
}
+5
4

RTTI. typeid, static_cast, dynamic_cast.

, , , RTTI, , , .

virtual bool operator == (const Shape &Other) const {
    if(typeid(Other) == typeid(*this))
    {
        const Circle& other = static_cast<const Circle&>(Other);
        // ...
    }
    else
        return false;
}

: typeid , , . .

, , , static_cast.

dynamic_cast ( , " " ), , " java" ), ( ). .

+8

, typeid . , , .

-

class ShapeVisitor
{
public:
    virtual void visitCircle(Circle const &) = 0;
    virtual void visitRectangle(Rectangle const &) = 0;
    // other shapes
}

Shape

virtual void acceptVisitor(ShapeVisitor &) = 0;

class CircleComparingVisitor : public ShapeVisitor
{
    Circle const & lhs; // shorthand for left hand side
    bool equal; // result of comparison
public:
    CircleComparingVisitor(Circle const & circle):lhs(circle), equal(false){}
    virtual void visitCircle(Circle const & rhs) {equal = lhs.radius == rhs.radius;}
    virtual void visitRectangle(Rectangle const &) {}
    // other shapes
    bool isEqual() const {return equal;}
}
// other shapes analogically

class ShapeComparingVisitor
{
    Shape const & rhs; // right hand side
    bool equal;
public:
    ShapeComparingVisitor(Shape const & rhs):rhs(rhs), equal(false) {}

    bool isEqual() const {return equal;}

    virtual void visitCircle(Circle const & lhs)
    {
        CircleComparingVisitor visitor(lhs);
        rhs.accept(visitor);
        equal = visitor.isEqual();
    }
    virtual void visitRectangle(Rectangle const & lhs)
    {
        RectangleComparingVisitor visitor(lhs);
        rhs.accept(visitor);
        equal = visitor.isEqual();
    }
}

operator==

bool Shape::operator==(const Shape &rhs) const
{
    ShapeComparingVisitor visitor(rhs);
    this->accept(visitor);
    return visitor->isEqual();
}

- operator== - ShapeComparingVisitor

-

Shape

virtual bool compareToCircle(Circle const &) const == 0;
virtual bool compareToRectangle(Rectangle const &) const == 0;

, ,

bool Circle::operator==(Shape const & rhs) const
{
    return rhs.compareToCircle(*this);
}
+4

, RTTI. , , , Shape&, , , , . , .

operator == , , , -, Shape&, .

, RTTI () , , RTTI. , ( , , , , , Shape), . , .

" ", , , - , , operator ==, , (, , , , ), , , RTTI.

- , .

+1

I feel that there is a fundamental violation of the Liskov signature principle, as you dig into the internal representations of objects. However, if you are happy to expose the internal representation of your objects (or you must do this for other reasons), then something like this will work.

class Shape
{
   virtual void std::string serialize() const =0;
   bool operator==( const Shape & s )
   {
      return this.serialize() == s.serialize();
   }
};
0
source

All Articles