为不同形状实现抽象'重叠'方法?

Mar*_*nen 2 c++ oop overriding class

我有一个名为的抽象基类Shape,它看起来像这样:

class Shape {
    public:
        Shape(Point center);
        virtual bool overlaps(Shape *other) = 0;

    private:
        Point m_center; // has getter&setter
};
Run Code Online (Sandbox Code Playgroud)

我遇到了这个overlaps(Shape *other);方法的问题; 我不知道如何在子类中实现它.

让我们举两个例子,(我可能只有两三个形状)CircleRect.基本上我试过的是在使用前向声明允许CircleRect"相互"知道之后在两个类中创建两个重载:

virtual bool Rect::overlaps(Circle *other);
virtual bool Rect::overlaps(Rect *other);
virtual bool Circle::overlaps(Circle *other);
virtual bool Circle::overlaps(Rect *other) { return other->overlaps(this); }
Run Code Online (Sandbox Code Playgroud)

现在很容易在所有重载中实现数学; 不过,我会得到一个错误cannot allocate an object of abstract type 'Circle'note: virtual bool Unit::overlaps(Unit *).这是因为我CircleRect类只有方法Circle *Rect *作为参数,但没有Unit *.

我也尝试着declarating CircleRectshape.h,但由于前向声明是不一样的类作为我的实际CircleRect,我只会得到同样的错误.

如果不删除公共基类,有没有办法实现这种行为?或者是否有解决方法使其工作?

附加信息

我有一个World包含的2D 类,vector<Shape *> m_shapes;我需要看看两个形状是否相互重叠;

for (unsigned int i = 0; i < m_shapes.size(); i++) {
    if (certainShape->overlaps(m_shapes[i])) {
        collapse();
    }
}
Run Code Online (Sandbox Code Playgroud)

das*_*ght 6

欢迎多方发货!从本质上讲,您要求的是一个相对于多个对象的运行时类型而言是虚拟的方法 - 在您的情况下,两个形状的类型正在测试重叠.

在C++中有几种常见的实现双重调度的方法:例如,您可以使用访问者模式,或者根据RTTI制作地图.选择一个或另一个取决于您.

如果您决定使用访问者模式,则可以Shape通过添加访问方法来"访问".

以下是基于访问者的方法示例.它无疑是相当冗长的,但它也解决了一个复杂的任务,所以它需要大量的代码是公平的.我将下面的示例剥离到最低限度 - 只有两个没有数据成员的形状,以及除了打印之外什么都不做的方法.这应该足以让你入门,但是:

#include <iostream>
using namespace std;

class ShapeVisitor;

struct Shape {
    virtual void accept(ShapeVisitor& v) = 0;
    virtual bool overlaps(Shape& other) = 0;
};

class Circle;
class Square;

struct ShapeVisitor {
    virtual void visitCircle(Circle& c) = 0;
    virtual void visitSquare(Square& s) = 0;
};

// These three methods do the actual work
bool checkOverlap(Square& s, Circle& c) {
    cout << "Checking if square overlaps circle" << endl;
    return false;
}
bool checkOverlap(Square& a, Square& b) {
    cout << "Checking if square overlaps square" << endl;
    return false;
}
bool checkOverlap(Circle& a, Circle& b) {
    cout << "Checking if circle overlaps circle" << endl;
    return false;
}

class Square : public Shape {
    struct OverlapVisitor : public ShapeVisitor {
        OverlapVisitor(Square& _my) : result(false), my(_my) {}
        virtual void visitCircle(Circle& c) {
            result = checkOverlap(my, c);
        }
        virtual void visitSquare(Square& s) {
            result = checkOverlap(my, s);
        }
        bool result;
        Square& my;
    };
public:
    virtual void accept(ShapeVisitor& v) {
        v.visitSquare(*this);
    }
    virtual bool overlaps(Shape& other) {
        OverlapVisitor v(*this);
        other.accept(v);
        return v.result;
    }
};

class Circle : public Shape {
    struct OverlapVisitor : public ShapeVisitor {
        OverlapVisitor(Circle& _my) : result(false), my(_my) {}
        virtual void visitCircle(Circle& c) {
            result = checkOverlap(my, c);
        }
        virtual void visitSquare(Square& s) {
            // Important: note how I switched the order of arguments
            // compared to Square::OverlapVisitor! There is only one
            // square/circle overlap function checker, and it expects
            // the square to be the first argument.
            result = checkOverlap(s, my);
        }
        bool result;
        Circle& my;
    };
public:
    virtual void accept(ShapeVisitor& v) {
        v.visitCircle(*this);
    }
    virtual bool overlaps(Shape& other) {
        OverlapVisitor v(*this);
        other.accept(v);
        return v.result;
    }
};
Run Code Online (Sandbox Code Playgroud)

这是关于ideone的这个运行演示.

使用RTTI方法,你可以创建一个map<pair<type_info,type_info>,checker>where checker是一个函数的类型,它接受两个指针Shape,并返回truefalse取决于形状是否重叠.您为每对对象类型创建一个这样的函数,根据type_info其预期的参数类型使用指向这些函数的指针填充映射,并在运行时使用此映射来调用所需的函数.

更有效的C++书籍第31项深入介绍了这两种方法,并附有一些很好的例子.实际上,本书中讨论的用于检测一对游戏对象之间冲突的用例与您正在实现的用例类似.