C++继承问题
我在应用程序架构中遇到以下问题,并且愿意解决它(抱歉有很多文字)。
我正在构建一个游戏引擎原型,并且我有基本抽象类AbstractRenderer
(我将使用 C++ 语法,但问题仍然很普遍)。
假设此渲染器有一些派生实现,例如 DirectxRenderer
和 OpenglRenderer
.
现在,假设这些渲染器中只有一个(让我们坚持基于 DirectX)有一个名为 IDirect3D9Device* m_device;
的成员,显然此时一切都很好- m_device
在 DirectxRenderer
内部使用,不应在抽象 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这可能是模板模式(不要与 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.
我不明白你的代码想要达到什么目的。您将 Renderable 对象派生为 DirectXRenderables 和 OpenGLRenderables,然后在派生自 Renderer 的对象中提供 OpenGL 或 DirectX 功能。可以说,一个特定的事物使用另一个特定的事物。
确定通用渲染函数、将它们设为抽象渲染器的纯虚拟成员并在 DirectXRenderer 和 OpenGLRenderer 中实现它们似乎更合理。然后,
IRenderable
将具有一个大致如下所示的成员函数绘制: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 inDirectXRenderer
andOpenGLRenderer
. Then aIRenderable
would have a member function draw roughly like this:使用模板,您可以将 IRendable 分成两个类,一个类对应两种渲染器类型中的每一种。这可能不是最好的答案,但它确实避免了动态转换的需要:
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:
使用 setter 设置渲染器变量并将其转换为该位置的正确类型。
Use a setter to set the renderer var and cast it to the proper type in that one place.
看看 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.
让我们远离编译器并考虑理论。如果
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
expectsDirectxRenderer
as parameter and not anyAbstractRenderer
, then some otherOtherGeometricalObject::Render
will probably expectOtherRenderer
object as parameter.So, different implementations of
AbstractGeometricalObject
have different signatures of theirRender
methods. If they are different, then there is no purpose in defining the virtualAbstractGeometricalObject::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 becausedynamic_cast
would fail.