方法参数中的动态类型匹配

时间:2016-09-06 22:53:12

标签: c++ oop derived-class visitor

我有基类Shape和从它继承的一些子类,如Circle,Rectangle,AlignedRectangle,ConvexPolygon,ConcavePolygon等。我希望每个类都有方法 bool intersect(Shape &other); 检查两个形状是否相交。我希望对不同的类对具有不同的这种方法的实现,因为一些交叉点的​​计算速度比蛮力方法快得多。 我想要这样的方法,所以我可以交叉两个Shape *指针而不关心实际里面的类型。

这是我目前的方法。

class Circle;
class Rect;

class Shape {
 public:
    Shape() {}

    virtual bool intersect(Shape &a) = 0;
    virtual bool intersect_circle(Circle &a) = 0;
    virtual bool intersect_rect(Rect &a) = 0;
};

class Circle : public Shape {
 public:
    Circle() {}
    virtual bool intersect(Shape &a);
    virtual bool intersect_circle(Circle &a);
    virtual bool intersect_rect(Rect &a);
};

class Rect : public Shape {
 public:
    Rect() {}
    virtual bool intersect(Shape &a);
    virtual bool intersect_circle(Circle &a);
    virtual bool intersect_rect(Rect &a);
};

bool Circle::intersect(Shape &a) {
    a.intersect_circle(*this);
}

bool Circle::intersect_circle(Circle &a) {
    cout << "Circle::intersect_circle" << endl;
}

bool Circle::intersect_rect(Rect &a) {
    cout << "Circle::intersect_rect" << endl;
}

bool Rect::intersect(Shape &a) {
    a.intersect_rect(*this);
}

bool Rect::intersect_circle(Circle &a) {
    cout << "Rect::intersect_circle" << endl;
}

bool Rect::intersect_rect(Rect &a) {
    cout << "Rect::intersect_rect" << endl;
}

还有其他一些'更好'的方法吗? 对于两个形状的交集可能没什么问题,但是如果我想让方法具有两个或多个可以具有任何类型的参数呢?这是3类案例的扩展:

void Circle::some_stuff(Shape &a, Shape &b) {
    a.some_stuff_circle(*this, b);
}

void OtherClass::some_stuff_circle(Circle &a, Shape &b) {
    b.some_stuff_circle_other_class(a, *this);
}

void AnotherClass::some_stuff_circle_other_class(Circle &a, OtherClass &b) {
    //do things here
}

会有如此多的冗余代码。我可能会遗漏一些基本的东西。

2 个答案:

答案 0 :(得分:1)

您可以使用变体类型。这是使用我的Polyvar标头的一种可能的解决方案。它不是最漂亮也不是最灵活的解决方案,但它确实允许您有选择地重载intersect并且仍然具有动态多态行为。我认为它和你进入C ++一样好。

注意:Polyvar依赖于Boost.Variant,如果按原样使用它。如果您想使用std :: variant或其他一些变体实现,请随意调整宏。

#include "polyvar.hpp"
#include <iostream>

// Define a variant template with a self-visiting member
// function named `intersect`. We'll use this to emulate a base class
DEFINE_POLYVAR(ShapeVariant, (intersect));

class Circle {

 public:

    Circle() {}

    template<typename T>
    bool intersect(T &a) {
        std::cout << "Circle to ???\n";
        auto your_implementation = false;
        return your_implementation;
    }
    bool intersect(Circle &a) {
        std::cout << "Circle to Circle\n";
        auto your_implementation = false;
        return your_implementation;
    }

    bool intersect(class Rect &a) {
        std::cout << "Circle to Rect\n";
        auto your_implementation = false;
        return your_implementation;
    }
};

class Rect {

 public:

    Rect(){}

    template<typename T>
    bool intersect(T &a) {
        std::cout << "Rect to ???\n";
        auto your_implementation = false;
        return your_implementation;
    }
    bool intersect(Circle &a) {
        std::cout << "Rect to Circle\n";
        auto your_implementation = false;
        return your_implementation;
    }

    bool intersect(Rect &a) {
        std::cout << "Rect to Rect\n";
        auto your_implementation = false;
        return your_implementation;
    }
};

class Triangle {

 public:

    Triangle(){}

    template<typename T>
    bool intersect(T &a) {
        std::cout << "Triangle to ???\n";
        auto your_implementation = false;
        return your_implementation;
    }

    bool intersect(Triangle &a) {
        std::cout << "Triangle to Triangle\n";
        auto your_implementation = false;
        return your_implementation;
    }
};

using Shape = ShapeVariant<Circle, Rect, Triangle /*, etc */>;

// Polyvar adds one level of visitation, but we must add another.
bool intersect(Shape& s1, Shape& s2) {

    auto visitor = [&s1](auto& s2) {
        return s1.intersect(s2);
    };

    return boost::apply_visitor(visitor, s2);
}

int main () {

    Shape s1 = Circle{};
    Shape s2 = Rect{};
    Shape s3 = Triangle{};

    intersect(s1, s2);
    intersect(s2, s1);
    intersect(s1, s3);
    intersect(s3, s2);
    intersect(s1, s1);
    intersect(s2, s2);
    intersect(s3, s3);
}

输出:

圆到矩形

Rect to Circle

圈到???

三角到???

圈到圈

Rect to Rect

Triangle to Triangle

Live example

另请参阅 - 多次发送:

顺便说一下,不要忘记编写代码const - 正确。

答案 1 :(得分:1)

为避免重复,我想出了这个:

#include <iostream>
#include <functional>
using namespace std;

class Circle;
class Rect;

class Shape {
public:
   enum Type
   {
      CircleType, RectType, UnknownType
   };

   Shape(Type t)
      : type_m(t)
   {}

   virtual ~Shape() {} // don't forget to declare it virtual!!
   bool intersect(Shape &a);
private:
   Type type_m;
};

bool Circle2Circle(const Shape& circle1, const Shape& circle2);
bool Circle2Rect(const Shape& circle, const Shape& rect);
bool Rect2Rect(const Shape& rect1, const Shape& rect2);

std::function<bool(const Shape&, const Shape&)> intersectFuncTable[Shape::UnknownType][Shape::UnknownType] =
{ 
   { Circle2Circle, Circle2Rect },
   { Circle2Rect, Rect2Rect }
};

bool Shape::intersect(Shape &a)
{
   return intersectFuncTable[type_m][a.type_m](*this, a);
}

class Circle : public Shape {
public:
   Circle() 
      : Shape(CircleType)
   {}
};

bool Circle2Circle(const Shape& circle1, const Shape& circle2)
{
   if (dynamic_cast<const Circle*>(&circle1) == nullptr ||
      (dynamic_cast<const Circle*>(&circle2)) == nullptr)
      return false;
   cout << "circle to circle\n";
  return true;
};

class Rect : public Shape {
public:
   Rect() 
      : Shape(RectType)  
   {}
};

bool Circle2Rect(const Shape& shape1, const Shape& shape2)
{
   if (dynamic_cast<const Circle*>(&shape1) != nullptr && dynamic_cast<const Rect*>(&shape2) != nullptr ||
      dynamic_cast<const Rect *>(&shape1) != nullptr && dynamic_cast<const Circle*>(&shape2) != nullptr)
   {
      cout << "circle to rect\n";
      return true;
   }
   return false;
};

bool Rect2Rect(const Shape& rect1, const Shape& rect2)
{
   if (dynamic_cast<const Rect*>(&rect1) == nullptr|| dynamic_cast<const Rect*>(&rect2) == nullptr)
      return false;
   cout << "rect to rect\n";
   return true;
}

int main()
{
   Circle cir;
   Rect rect;

   cir.intersect(rect);
   rect.intersect(cir);
   cir.intersect(cir);
   rect.intersect(rect);

   return 0;
}

这个想法是你实现了每个交叉函数,在添加新类型时,你可以在基类的枚举中添加一个类型,并添加所需的交叉函数。 另外 - 第二个想法 - 更好地使用type_m而不是动态转换,因为访问数据成员的成本更低。