问题描述
我得到了一个对象树。在对象树中,我存储场景节点。 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++ 技术上没有它们,但无论如何你可以制作它们。
看看虚拟继承。