2016-09-06 75 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而不是動態轉換,因爲訪問數據成員更便宜。