C++继承问题

发布于 2024-09-09 16:02:47 字数 2377 浏览 5 评论 0原文

我在应用程序架构中遇到以下问题,并且愿意解决它(抱歉有很多文字)。

我正在构建一个游戏引擎原型,并且我有基本抽象类AbstractRenderer (我将使用 C++ 语法,但问题仍然很普遍)。

假设此渲染器有一些派生实现,例如 DirectxRendererOpenglRenderer.

现在,假设这些渲染器中只有一个(让我们坚持基于 DirectX)有一个名为 IDirect3D9Device* m_device; 的成员,显然此时一切都很好- m_deviceDirectxRenderer 内部使用,不应在抽象 AbstractRenderer 超类中公开。

我还添加了一些抽象渲染接口,例如IRenderable。它意味着一个纯虚拟方法virtual void Render(AbstractRenderer* renderer) const = 0;


这就是一些问题开始的地方。假设我正在对某个场景进行建模,因此,该场景中可能会有一些几何对象。

我创建抽象超类 AbstractGeometricalObject 并派生基于 DirectX 的实现 DirectxGeometricalObject 。第二个将负责存储指向 DirectX 特定顶点和顶点的指针。索引缓冲区。

现在 - 问题所在。

AbstractGeometricalObject 显然应该派生 IRenderable 接口,因为它在逻辑上是可渲染的。

如果我从 AbstractGeometricalObject 派生我的 DirectxGeometricalObject,第一个应该具有 virtual void Render(AbstractRenderer* renderer) const { ... } 方法它,以及Abstract... 的东西带来了一些麻烦。

查看代码以获得更好的解释:

现在我的类看起来如下:

class AbstractGeometricalObject : public IRenderable {
    virtual void Render(AbstractRenderer* renderer) const { ... }
};

class DirectxGeometricalObject : public AbstractGeometricalObject {

    virtual void Render(AbstractRenderer* renderer) const {

    // I think it's ok to assume that in 99 / 100 cases the renderer
    // would be a valid DirectxRenderer object

    // Assume that rendering a DirectxGeometricalObject requires
    // the renderer to be a DirectxRenderer, but not an AbstractRenderer
    // (it could utilize some DX-specific settings, class members, etc

    // This means that I would have to ***downcast*** here and this seems really
    // bad to me, because it means that this architecture sucks

    renderer = dynamic_cast<DirectxRenderer*>(renderer);

    // Use the DirectX capabilities, that's can't be taken out
    // to the AbstractRenderer superclass
    renderer.DirectxSpecificFoo(...);
}

我知道我可能担心太多,但在如此简单的情况下这种沮丧意味着我可能会如果我的应用程序增长,我会被迫做出很多沮丧。

当然,我想避免这种情况,所以请您在设计方面给我一些更好的建议/指出我的错误。 当然,我想避免这种情况。 em>

谢谢

I have the following problem in application architecture and am willing to solve it (sorry for a lot of text).

I am building a game engine prototype and I have base abstract class AbstractRenderer (I will use C++ syntax, but still the problem is general).

Assume there are some derived implementations of this renderer, let's say DirectxRenderer and OpenglRenderer.

Now, let's say that only one of these renderers (let's stick to DirectX-based) has a member called IDirect3D9Device* m_device; Obviously at this point everything is fine - m_device is used internally in DirectxRenderer and shouldn't be exposed in the abstract AbstractRenderer superclass.

I also add some abstract rendering interface, for instance IRenderable. It means simply one pure virtual method virtual void Render(AbstractRenderer* renderer) const = 0;


And this is the place where some problems start. Assume I am modelling some scene, so, this scene will probably have some geometrical objects in it.

I create abstract superclass AbstractGeometricalObject and derived DirectX-based implementation DirectxGeometricalObject. The second one would be responsible for storing pointers to DirectX-specific vertex & index buffers.

Now - the problem.

AbstractGeometricalObject should obviously derive the IRenderable interface, because it's renderable in logical terms.

If I derive my DirectxGeometricalObject from AbstractGeometricalObject, the first one should have virtual void Render(AbstractRenderer* renderer) const { ... } method in it, and that Abstract... stuff brings some troubles.

See the code for better explanation:

And for now my classes look the following way:

class AbstractGeometricalObject : public IRenderable {
    virtual void Render(AbstractRenderer* renderer) const { ... }
};

class DirectxGeometricalObject : public AbstractGeometricalObject {

    virtual void Render(AbstractRenderer* renderer) const {

    // I think it's ok to assume that in 99 / 100 cases the renderer
    // would be a valid DirectxRenderer object

    // Assume that rendering a DirectxGeometricalObject requires
    // the renderer to be a DirectxRenderer, but not an AbstractRenderer
    // (it could utilize some DX-specific settings, class members, etc

    // This means that I would have to ***downcast*** here and this seems really
    // bad to me, because it means that this architecture sucks

    renderer = dynamic_cast<DirectxRenderer*>(renderer);

    // Use the DirectX capabilities, that's can't be taken out
    // to the AbstractRenderer superclass
    renderer.DirectxSpecificFoo(...);
}

I know I'm probably worrying too much, but this downcast in such a simple case means that I could be forced to make lots of downcasts if my application grows.

Definitely, I would like to avoid this, so please, could you advice me something better in design terms / point out my errors.

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

笑脸一如从前 2024-09-16 16:02:47

这可能是模板模式(不要与 C++ 模板混淆)派上用场的情况。抽象类中的公共Render应该是非虚拟的,但让它调用私有虚拟函数(例如DoRender)。然后在派生类中,您重写 DoRender。

这是一篇文章,深入描述了具有私有虚拟函数的模板模式的使用。

编辑:

我开始整理一个例子来表达我的意思,看起来该架构实际上存在一个更广泛的缺陷。您对 AbstractRenderer 的使用有点无聊,因为您迫使每个几何对象密切了解特定的渲染器类型。

渲染器应该能够使用 Renderable 的公共方法,或者 Renderable 应该能够使用渲染器的公共方法。或者,如果确实需要如此密切的连接,也许您可​​以为具体渲染器提供一个 Renderable 工厂。我确信还有一些其他的图案也很适合。

This might be a situation where the template pattern (not to be confused with C++ templates) comes in handy. The public Render in the abstract class should be non-virtual, but have it call a private virtual function (e.g. DoRender). Then in the derived classes, you override DoRender instead.

Here's an article that goes into great depth describing the use of template pattern with private virtual functions.

Edit:

I started to put together an example of what I meant, and it seems like there's actually a broader flaw in the architecture. Your use of AbstractRenderer is somewhat frivolous since you're forcing each geometricalobject to be intimately aware of a particular renderer type.

Either the renderer should be able to work off the public methods of Renderables, or Renderables should be able to work off the public methods of the Renderer. Or perhaps you can give the concrete renderers a Renderable factory if there really needs to be such an intimate connection. I'm sure there are some other patterns that would fit well, too.

鼻尖触碰 2024-09-16 16:02:47

我不明白你的代码想要达到什么目的。您将 Renderable 对象派生为 DirectXRenderables 和 OpenGLRenderables,然后在派生自 Renderer 的对象中提供 OpenGL 或 DirectX 功能。可以说,一个特定的事物使用另一个特定的事物。
确定通用渲染函数、将它们设为抽象渲染器的纯虚拟成员并在 DirectXRenderer 和 OpenGLRenderer 中实现它们似乎更合理。然后,IRenderable 将具有一个大致如下所示的成员函数绘制:

void draw(const AbstractRenderer& r) {
  //general stuff
  r.drawLine(...);
  //only possible on directX
  if(DirectxRenderer rx = dynamic_cast<DirectxRenderer*>(r)) {
  //...
  } else {
    //throw exception or do fallback rendering in case we use something else
  }
} 

I don't see what your code wants to achieve. You derive Renderable objects to DirectXRenderables and OpenGLRenderables and then provide OpenGL or DirectX functionality in something derived from Renderer. A specific thing uses another specific thing so to speak.
It would seem much more reasonable to identify general rendering functions, make them pure virtual members of your abstract renderer and implement them in DirectXRenderer and OpenGLRenderer. Then a IRenderable would have a member function draw roughly like this:

void draw(const AbstractRenderer& r) {
  //general stuff
  r.drawLine(...);
  //only possible on directX
  if(DirectxRenderer rx = dynamic_cast<DirectxRenderer*>(r)) {
  //...
  } else {
    //throw exception or do fallback rendering in case we use something else
  }
} 
游魂 2024-09-16 16:02:47

使用模板,您可以将 IRendable 分成两个类,一个类对应两种渲染器类型中的每一种。这可能不是最好的答案,但它确实避免了动态转换的需要:

template <typename RendererType>
struct IRenderable {
    virtual void Render(RendererType* renderer) const = 0;
}

template <typename RendererType>
class AbstractGeometricalObject : public IRenderable<RendererType> {
    virtual void Render(RendererType* renderer) const { ... }
};

class DirectxGeometricalObject : public AbstractGeometricalObject<DirectxRenderer> {
  // this class will now require a void Render(DirectxRenderer* renderer)
}

Using templates, you could split the IRendable into two classes, one for each of the two renderer types. This is probably not the best answer, but it does avoid the need for the dynamic cast:

template <typename RendererType>
struct IRenderable {
    virtual void Render(RendererType* renderer) const = 0;
}

template <typename RendererType>
class AbstractGeometricalObject : public IRenderable<RendererType> {
    virtual void Render(RendererType* renderer) const { ... }
};

class DirectxGeometricalObject : public AbstractGeometricalObject<DirectxRenderer> {
  // this class will now require a void Render(DirectxRenderer* renderer)
}
土豪我们做朋友吧 2024-09-16 16:02:47

使用 setter 设置渲染器变量并将其转换为该位置的正确类型。

Use a setter to set the renderer var and cast it to the proper type in that one place.

江心雾 2024-09-16 16:02:47

看看 Bridge 设计模式是否可以帮助您:“将抽象与其实现分离,以便两者可以独立变化。”在您的示例中,AbstractGeometricalObject 将指向一个实现,一个具有特定于平台的子类的纯虚拟接口。棘手的部分是花时间发现该界面。

See if the Bridge design pattern helps you: "Decouple an abstraction from its implementation so that the two can vary independently." In your example, AbstractGeometricalObject would point to an implementation, a pure virtual interface with platform-specific subclasses. The tricky part is taking the time to discover that interface.

栖迟 2024-09-16 16:02:47

让我们远离编译器并考虑理论。如果 DirectxGeometricalObject::Render 期望 DirectxRenderer 作为参数,而不是任何 AbstractRenderer,则其他一些 OtherGeometricalObject::Render 将可能需要 OtherRenderer 对象作为参数。

因此,AbstractGeometricalObject 的不同实现具有不同的 Render 方法签名。如果它们不同,则定义虚拟 AbstractGeometricalObject::Render 就没有意义。

如果您声明 AbstractGeometricalObject::Render(AbstractRenderer*),那么您应该能够将任何渲染器传递给任何几何对象。就您而言,您不能,因为 dynamic_cast 会失败。

Let's distance from compilers and consider theory. If DirectxGeometricalObject::Render expects DirectxRenderer as parameter and not any AbstractRenderer, then some other OtherGeometricalObject::Render will probably expect OtherRenderer object as parameter.

So, different implementations of AbstractGeometricalObject have different signatures of their Render methods. If they are different, then there is no purpose in defining the virtual AbstractGeometricalObject::Render.

If you declare AbstractGeometricalObject::Render(AbstractRenderer*), then you should be able to pass any renderer to any geometrical object. In your case, you can't because dynamic_cast would fail.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文