2

という抽象基本クラスがShapeあります。これは次のようになります。

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

    private:
        Point m_center; // has getter&setter
};

overlaps(Shape *other);メソッドに問題があります。サブクラスに実装する方法がわかりません。

2 つの例を考えてみましょう (おそらく、形状は 2 つまたは 3 つしかないでしょう)CircleRect. 基本的に私が試みたのは、前方宣言を使用してお互いを許可Circleし、「知る」ことを使用した後、両方のクラスに 2 つのオーバーロードを作成することです。Rect

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); }

すべてのオーバーロード内で数学を簡単に実装できるようになりました。ただし、エラーが表示cannot allocate an object of abstract type 'Circle'されnote: virtual bool Unit::overlaps(Unit *)ます。これは、 myCircleとクラスにはパラメータとしてandをRect持つメソッドしかなく、 を持つメソッドがないためです。Circle *Rect *Unit *

また、shape.hCircleでandを前方宣言しようとしましたが、前方宣言は実際のand と同じクラスではないため、同じエラーのみが発生します。RectCircleRect

共通の基本クラスを削除せずに、そのような動作を実装する方法はありますか? または、それを機能させるための回避策はありますか?

追加情報

を含む 2DWorldクラスがvector<Shape *> m_shapes;あり、2 つの形状が互いに重なっているかどうかを確認する必要があります。

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

3 に答える 3

6

複数発送歓迎!基本的に、複数のオブジェクトの実行時の型に関して仮想的なメソッドを求めています。あなたの場合、重複についてテストされている2つの形状の型です。

C++ でダブル ディスパッチを実装するには、いくつかの一般的な方法があります。たとえば、訪問者パターンを使用したり、 RTTIに基づいてマップを作成したりできます。どちらを選択するかはあなた次第です。

訪問者パターンを使用することにした場合はShape、訪問メソッドを追加して「訪問可能」にします。

訪問者ベースのアプローチの例を次に示します。確かにかなり冗長ですが、複雑なタスクにも対応しているため、多くのコードが必要になるのは当然です。以下の例では、データ メンバーを持たない 2 つの図形と、印刷以外のことを何も行わないメソッドのみを最小限に抑えています。ただし、これで開始するには十分なはずです。

#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;
    }
};

これが ideone で実行中のデモです。

RTTI アプローチでは、 where チェッカーを への 2 つのポインターを受け取り、形状が重なるかどうかに応じてorを返すmap<pair<type_info,type_info>,checker>関数の型にすることができます。オブジェクト型のペアごとにそのような関数を 1 つ作成し、予想されるパラメーター型に基づいてこれらの関数へのポインターをマップに入力し、実行時にこのマップを使用して目的の関数を呼び出します。Shapetruefalsetype_info

More Effects C++ bookの Item 31では、これら両方のアプローチについて詳細に説明し、いくつかの優れた例を示しています。実際、この本で説明されているゲーム オブジェクトのペア間の衝突を検出するユース ケースは、あなたが実装しているものと似ています。

于 2013-07-16T11:14:36.827 に答える
1

必要なのは、「どれだけ大きいかother」タイプの関数です。非常にシンプルにして、バウンディング ボックス (形状全体を覆うのに十分な大きさの四角形) を使用すると、次のようになります。

(簡単にrectするために、長方形の用語として使用しています)

class Shape
{
 ...
 virtual rect BoundingBox() = 0;

 bool overlaps(const Shape& other)
 {
     return BoundingBox.FitsInside(other.BoundingBox()); 
 }

};

明らかに、fitsinside2 つの長方形とBoundingBox各形状の関数を作成する必要がありますが、それほど難しくはありません。

「これはこれでStar完全にカバーされていOvalますか?」を作るために。少し難しい解決策になります[両方の形状の完全な輪郭が必要であり、Oval正確に楕円形にするためには輪郭がかなり多くの点になる場合があります]。

于 2013-07-16T11:12:20.253 に答える