我有基类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
}
会有如此多的冗余代码。我可能会遗漏一些基本的东西。
答案 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
另请参阅 - 多次发送:
顺便说一下,不要忘记编写代码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而不是动态转换,因为访问数据成员的成本更低。