C++访问者模式多组件

问题描述

我得到了一个对象树。在对象树中,我存储场景节点。 SceneNode 通常是其他类的基类。 我想为代表场景节点的对象实现不同的行为。

这个问题的正确模式应该是访问者模式。我想遍历 SceneNodes 并想根据存储在 SceneNodes 后面的对象调用不同的函数。 但我不仅希望允许对象树中的一个对象成为访问者模式的一个组件,而且还希望共享功能

例如:我有一个 BaSEObject。我可以更新这个 BaSEObject(例如到一个新位置),我可以绘制一个 BaSEObject(OpenGL 的东西)。 但我也有一个 Camera 对象。相机对象可以更新但不能绘制。

这是访问者资料的实现:

class Visitor 
{
public:
    virtual void VisitUpdate(ComponentUpdate* element) = 0;
    virtual void VisitDraw(ComponentDraw* element) = 0;
    virtual void VisitOverlay(ComponentOverlay* element) = 0;
};

访问者组件:

class Component
{
public:
    virtual ~Component() { }
    virtual void accept(Visitor* visitor) = 0;
};

具体组件:

class ComponentUpdate : public Component
{
public:
    void accept(Visitor* visitor) override {
        visitor->VisitUpdate(this);
    }

    virtual void update() = 0;
};

class ComponentDraw : public Component
{
public:
    void accept(Visitor* visitor) override {
        visitor->VisitDraw(this);
    }

    virtual void draw() = 0;
};

最后是一个具体的访客:

class SceneNodeVisitor : public Visitor
{
    void VisitUpdate(ComponentUpdate* element) override {
        element->update();
    }

    void VisitDraw(ComponentDraw* element) override {
        element->draw();
    }
};

现在我想做这样的事情:

class Camera : public ComponentUpdate
{
    void update() override { std::cout << "Camnera update" << std::endl; }
};

class ObjectBase : public ComponentDraw,public ComponentUpdate
{
    void update() override { std::cout << "ObjectBase update" << std::endl; }
    void draw() override { std::cout << "ObjectBase draw" << std::endl; }
};

好的,到目前为止一切顺利。我现在的问题是编译器说“基类不明确”。我认为这是不正确的,因为 ObjectBase 是不明确的,因为它有两个不同的 accept() 函数,对吗?

有没有办法使用访问者模式,以便我可以自由地为类添加我需要的功能

这里的主要功能

int main() {
    ObjectBase ob;
    Camera cam;
    SceneNodeVisitor visitor;

    std::vector<Component*> components;
    components.push_back(new Camera);
    components.push_back(new ObjectBase);
    components[0]->accept(&visitor);
    components[1]->accept(&visitor);
}

奇怪的是我可以在堆栈上创建ObjectBase。如果我尝试在堆上创建对象(通过 new),我只会收到错误消息。

Pastebin 目前已关闭,我可以在它再次启动后立即提供此示例代码

解决方法

好吧,我不完全确定,但我认为你应该把你正在做的一些概念分开。

一旦你继承了两个都继承自同一个基类的类,你就需要开始研究虚拟继承。那可能会解决您的问题。但是从 ObjectBase 到 Component 的路径是通过 ComponentDraw 或 ComponentUpdate。实际上,您可能有两个 Component 副本,因为您没有使用虚拟继承。

我会强烈考虑使用接口的概念。虽然 C++ 技术上没有它们,但无论如何你可以制作它们。

看看虚拟继承。