假设我有一个Actor类型,它可以是我游戏关卡中的任何可放置对象。 我也有单位,这是一个儿童类的演员。单位可以是玩家或AI控制的英雄。
Unit还有2个子类:玩家(玩家)和英雄(AI控制单位)。 在Unit类中,将有Player和Hero都需要的移动信息,旋转和其他常规设置。在子类中,AI将具有与玩家不同的功能。
现在我面临以下问题:
一个函数只接受Actor作为参数(例如OnOverlap(Actor a*)
)
但是OnOverlap()
只应该做一些事情,如果它是一个单元类(英雄或玩家)。
因此,我需要在C ++中使用Java中的instanceof()
。
解决方法是使用dynamic_cast
,但我不确定这是否对性能有好处。或者使用虚拟,但是当Hero比Player有更多功能时,这不会起作用。
或者我应该尝试全新的OOP设计?
答案 0 :(得分:2)
我说dynamic_cast
是code smell。本质上没有必要,但表明您的设计可能出现问题。
OOP中的一个重要概念是多态性:对象根据其类型行为不同的想法,以及此行为是封装,隐藏在接口后面。如果你明确地检查一个对象的类型来改变你想要应用它的逻辑,那么你就是在违反多态。
现在虚拟方法也不是很好,它们确实会产生运行时成本,有时会给表带来太多的复杂性。 C ++使virtual
方法成为异常,而非默认,我相信这些原因。
您应该知道的一件事是,虚方法只是一种多态,称为动态多态。另一种获取不同行为的方法取决于对运行时较少阻碍的类型:静态多态,AKA template metaprogramming。但这对复杂性方面并没有帮助。
现在你在这种情况下应该做的是分开处理单独的事情。您可能希望在OnOverlap()
上使用此Units
方法是有原因的:让我们说您仅对Units
进行碰撞检查,而不是全部Actors
}。然后维护一个单独的Units
列表,并在OnOverlap()
类上使该Unit
方法非虚拟。以这种方式思考往往是关键。
答案 1 :(得分:0)
此技术称为Run-Time Type Information (RTTI),并检查dynamic_cast的输出是否为NULL是有效的实现。
另一种方法是#include <typeinfo>
并使用typeid(*a).name()
来获取类型的字符串名称。
答案 2 :(得分:0)
使用dynamic_cast
的需要经常(尽管并非总是)表明您存在设计缺陷。在这种情况下,我认为你这样做。
如果没有进一步评论您所描述的架构的整体设计,我会说使用虚拟方法是正确的方法。让OnOverlap
接受它的演员并在actor上调用OnOverlapped
方法,也许给它一个指向执行初始OnOverlap
方法的东西的指针(它不是从你的问题中清楚那是什么(*))。
void X::OnOverlap (Actor * actor) {
actor->OnOverlapped(this);
}
OnOverlapped
本身就是虚拟的。这可以让你做正确的事情&#34;在演员单元的情况下,但在其他地方什么也不做或其他一些默认行为。你是对的,在这种情况下,这意味着你只能使用Actor
的公共API。您必须将需要Player
其他方法的任何内容移至Player
OnOverlapped
的{{1}}实现中。如果你真的认为你出于某些原因不能这样做,你或许应该考虑更高层次的替代设计(可能需要编辑你的问题以提供有关你的架构的更多细节)。
(*)我对您的问题的初步阅读让我觉得OnOverlap
已经不是Actor
的方法了;它是不相关的东西的一部分。如果 是Actor
的一部分,您可能会对名为double dispatch的模式感兴趣,该模式可用于此类&#34;碰撞处理&#34;问题的类型,也是一种使用动态调度(虚拟)的技术。
答案 3 :(得分:0)
这些功能由我的引擎预定义,参数必须在此处 但是父函数“Actor”。所以如果任何一个Actor与之相撞 网格,该函数被调用。在这种情况下,我没有看到别的 解决方案,而不是让Actor对象的实例知道它是否 只是其他东西的玩家
Actor基类中的一个简单虚函数可以是一个noop。在Unit类中重写它,如果需要,在Hero和Player中进一步专门化它。非单位演员将调用一个空方法。比RTTI和惯用语更清洁。
可以使用一个默认的基本实现,它希望被专门的后代覆盖。这是抽象基类的重点,除非在这种情况下,您提供的默认实现不执行任何操作。这个想法是你要发送“DoCollision”消息(C ++中的虚函数)。您不关心哪些类处理它或如何处理它。只是该对象具有DoCollision编译时“接口”并且您正确地发送了它。