2016-09-06 40 views
2

我有基类形状并从它继承像圆,矩形,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 
} 

这会有很多冗余代码。我可能会错过一些基本的东西。

+0

您已重新创建访问者模式。顺便说一句,多重参数动态调度是Bjarne写的,因此将来可能会出现在标准C++中。您可能想搜索“C++中的multimethods” –

回答

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

的Rect金环

金环???

三角形???

圈至一圈

矩形到矩形

三角到三角


Live example


又见 - 多分派:


顺便说一句,别忘了让你的代码const -correct。

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而不是动态转换,因为访问数据成员更便宜。