1

私はC++で小さなシミュレーションフレームワークを作成しています。純粋なシミュレーション関連のものを表示から分離したいと思います。だから私は次のようなクラスを持っています:

class Pointer : public SimulationObject {};
class Particle : public SimulationObject {};
class LearningObserver : public SimulationObject {};

これらはすべてSimulationObjectから派生していますが、視覚的に表現できるのは一部(!)のみです。

class Renderable {
public:
    virtual void render() const = 0;
//may also include reference to assosciated SimulationObject
}
class PointerRenderable : public Renderable {
    void render() const { std::cout << "Render a pointer" << std::endl;
};
class ParticleRenderable : public Renderable {
    void render() const { std::cout << "Render a particle" << std::endl;
};

これで、新しいSimulationObjectが(実行時に)シミュレーションに追加されるたびに、それをレンダリングするクラスがあるかどうかを確認したいと思います。もしそうなら、私はそれのインスタンスを作成したいと思います。私の最初のアイデアは、ポリモーフィを使用することでした。

class AbstractRenderFactory {
   virtual Renderable * provideRenderable(SimulationObject * so) const = 0;
};

class ConcreteRenderFactory {
   void PointerRenderable * provideRenderable(Pointer * pointer) {
       return new PointerRenderable();
   }
   // further provideRenderable's
};

ただし、メソッドをオーバーライドするときに派生型を使用することは明らかに機能しません(基本メソッドが呼び出されます)。また、関数は部分的にしか定義されないため、これには問題があります。

別のアイデアは、タイプの実際のマッピングを提供することでした。

std::map<std::type_index, std::type_index> renderTable;

しかし、type_infoを使用するだけではインスタンスを取得できません。アイデアはありますか?


更新:ビジターパターンを使用してみましたが、同様の問題が発生しました。私の訪問者クラスは次のようになります。

class RenderVisitor {
public:
    virtual Renderable * visit(SimulationObject * so) {
         // I would like to have this method abstract, but I could live with this
         return 0;
    }
};

class MyRenderVisitor : public RenderVisitor {
public:
    Renderable * visit(Pointer * pointer) const {
        return new PointerRenderable();
    }
};

さらに、シミュレーションパーツの新しい基本クラス。

class SimulationObject {
public:
    Renderable * accept(RenderVisitor * renderer) {
        return renderer->visit(this);
    }
};

MyRenderVisitorのvisit-methodは明らかに基本クラスのオーバーライドとして認識されませんが、これが実際の(派生)タイプを指しているので、正しいメソッド(MyRenderVisitorのメソッド)が呼び出されることを期待していまし

私のテストシナリオは次のようになります。

RenderVisitor * rv = new MyRenderVisitor();
SimulationObject * pointer = new Pointer();

Renderable * renderable = pointer->accept(rv);
renderable->render();
// renderable = 0 -> seg-fault

それとも私の実装が間違っているだけですか?

4

1 に答える 1

1

はい、ダブルディスパッチ手法が必要なようです。Visitorパターンを適用することで実装できます。アイデアを説明するコード:

インターフェース部分:

class SimulationObject {
public:
    virtual void Accept(RenderableVisitor* visitor) const = 0;
    ...
};

class RenderableVisitor {
public:
    virtual void Visit(Pointer* pointer) const = 0;
    virtual void Visit(Particle* particle) const = 0;
    virtual void Visit(LearningObserver* learning_observer) const = 0;
}

コンクリート部​​分:

class Pointer : public SimulationObject {
public:
    virtual void Accept(RenderableVisitor* visitor) const
    {
        visitor->Visit(this);
    }
};

class Particle : public SimulationObject {
public:
    virtual void Accept(RenderableVisitor* visitor) const
    {
        visitor->Visit(this);
    }
};

class LearningObserver : public SimulationObject {
public:
    virtual void Accept(RenderableVisitor* visitor) const
    {
        visitor->Visit(this);
    }
};

class ConcreteRenderableVisitor : public RenderableVisitor {
public:
    virtual void Visit(Pointer* pointer) const
    {
        std::cout << "Render a pointer" << std::endl;
    }

    virtual void Visit(Particle* particle) const
    {
        std::cout << "Render a particle" << std::endl;
    }

    virtual void Visit(LearningObserver* learning_observer) const
    {
        std::cout << "Render a learning observer" << std::endl;
    }
}

クラスは、クラス階層ConcreteRenderableVisitorの各リーフのロジックを実装します。SimulationObject

クライアントコード:

ConcreteRenderableVisitor visitor;
Particle particle;
particle.Accept(&visitor); // Render a particle by the visitor.

クライアントコード2(テクニックを証明するためのより抽象的な例):

RenderableVisitor* visitor = new ConcreteRenderableVisitor;
SimulationObject* object = new Particle;
object->Accept(visitor);
于 2013-01-27T09:03:26.070 に答える