代码之家  ›  专栏  ›  技术社区  ›  Yippie-Ki-Yay

C++继承问题

  •  1
  • Yippie-Ki-Yay  · 技术社区  · 15 年前

    我在应用程序体系结构中有以下问题,并愿意解决它 (很抱歉收到很多短信)。

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

    假设这个渲染器有一些派生的实现,比如 DirectxRenderer OpenglRenderer .

    现在,假设只有一个这样的渲染器(我们坚持使用基于DirectX的)有一个名为 IDirect3D9Device* m_device; 显然在这一点上一切都很好- m_device 在内部使用 直接渲染器 不应该被抽象地暴露出来 抽象呈现器 超类。

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


    这就是一些问题开始的地方。假设我正在为一些场景建模,那么这个场景中可能会有一些几何对象。

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

    现在-问题。

    抽象几何对象 显然应该推导出 无可救药的 接口,因为它 可渲染的 用逻辑术语来说。

    如果我得到我的 DirectX几何对象 抽象几何对象 第一个应该有 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(...);
    }
    

    我知道我可能担心的太多了,但是这种简单的情况下的降级意味着如果我的应用程序增长,我可能会被迫进行大量的降级。

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

    谢谢你

    6 回复  |  直到 15 年前
        1
  •  3
  •   Cogwheel    15 年前

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

    这篇文章对 template pattern with private virtual functions .

    编辑:

    我开始举出一个例子来说明我的意思,这看起来在体系结构中有一个更广泛的缺陷。抽象渲染器的使用有些琐碎,因为您强制每个几何对象密切注意特定的渲染器类型。

    渲染器应该能够处理渲染器的公共方法,或者渲染器应该能够处理渲染器的公共方法。或者,如果真的需要如此亲密的联系,你可以给混凝土渲染器一个可渲染的工厂。我相信还有其他一些模式也很适合。

        2
  •  2
  •   pmr    15 年前

    我不知道您的代码想要实现什么。您将可渲染对象派生为DirectXrenderables和OpenGLrenderables,然后在从渲染器派生的内容中提供OpenGL或DirectX功能。可以说,一件特定的事情使用另一件特定的事情。 识别一般的渲染函数,使它们 pure virtual 抽象呈现器的成员并在中实现它们 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
      }
    } 
    
        3
  •  1
  •   Aaron McDaid    15 年前

    使用模板,您可以将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)
    }
    
        4
  •  0
  •   gtrak    15 年前

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

        5
  •  0
  •   Jon Reid    15 年前

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

        6
  •  0
  •   zvone    15 年前

    让我们远离编译器,考虑一下理论。如果 DirectxGeometricalObject::Render 期待 DirectxRenderer 作为参数而不是任何参数 AbstractRenderer ,然后是其他的 OtherGeometricalObject::Render 可能会期待 OtherRenderer 对象作为参数。

    所以,不同的 AbstractGeometricalObject 他们有不同的签名 Render 方法。如果它们不同,那么定义虚拟 AbstractGeometricalObject::Render .

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