问题描述
我正在学习使用虚幻引擎开发游戏的 C++。
我正在使用从 C++ equivalent of instanceof 复制的下一个代码来检查对象是否是类的实例:
template<typename Base,typename T>
inline bool instanceof(const T*) {
return std::is_base_of<Base,T>::value;
}
void OnHit(UPrimitiveComponent* HitComp,AActor* OtherActor,UPrimitiveComponent* OtherComp,FVector normalImpulse,const FHitResult& Hit);
在上述方法中,我想检查 OtherActor
是否是类 Paddle
的实例。 Paddle
继承自 APawn
,后者继承自 AActor
。
当 OtherActor
是 APaddle
实例时,以下代码返回 false:
if (instanceof<APaddle>(OtherActor))
为了检查 instanceof
是否有效,我尝试了以下代码,并且有效(返回 true):
if (instanceof<AActor>(OtherActor))
为什么它不适用于子类 (APaddle
)?
解决方法
您的 isinstanceof
只会检查静态情况。请注意,您的函数的结果实际上并不取决于参数 isinstanceof
,即对象本身。它只取决于参数/对象的类型。
您需要的是动态检查。为此,有 dynamic_cast
。
像这样
template<typename Base,typename T>
inline bool instanceof(const T* t) {
return dynamic_cast<const Base*>(t) != nullptr;
}
但请注意:要使其正常工作,您需要 Base
中的虚函数。如果 Base
不是虚拟基类,则 C++ 中无法执行 instanceof 检查。
在提出 SO 问题时,一个最小的完整可验证示例通常是一个好主意。这是我的尝试:
struct AActor {};
struct APawn:AActor {};
struct APaddle:APawn {};
APaddle paddle;
AActor* OtherActor = &paddle;
assert( instanceof<APaddle>(OtherActor) );
assert( instanceof<AActor>(OtherActor) );
第一个断言是假的,第二个是真的,你想知道为什么吗?
std::is_base_of
检查类型信息,而不是实例信息。
return std::is_base_of<Base,T>::value;
在 APaddle
的情况下,这是
return std::is_base_of<APaddle,AActor>::value;
询问“APaddle
是 AActor
的基类吗?”很明显不是。
在 AActor
情况下:
return std::is_base_of<AActor,AActor>::value;
C++ 认为 T
是 T
的基类——你是你自己的基类(!)——至少在这个特性中是这样。所以它返回真。
函数从不检查传入的指针,因此您的 AActor
指针实际上是 APaddle
的事实无关紧要。
如果您想动态测试类型是否匹配,您 (a) 必须确保您的类型是多态的,并且 (b) 应该重新考虑,以及 (c) 使用 dynamic_cast
。
struct AActor {
virtual ~AActor() {}
};
struct APawn:AActor {};
struct APaddle:APawn {};
// is the argument pointing to an instance of the type X?
template<class X,class T>
bool instanceof(T const* ptr) {
return nullptr != dynamic_cast<X const*>(ptr);
}
现在您的代码按预期工作。请注意,dynamic_cast
并不总是有效;在某些情况下,它是不明确的,无法进行类型转换,或者类型不够多态。 C++ 不要求所有对象(无论是结构体还是类)都支持内置的运行时类型信息 (RTTI),并且不支持它的类型无法(通过动态转换)查询它们的真实类型。
对于 (b),查询多态对象的确切类型违反了 LSP,并将继承层次结构与算法实现以一种不能很好扩展的方式耦合。
举一个由 (b) 引起的例子,假设你有水果,水果最初可以是苹果或橙子。
您使用一堆动态转换编写代码,以确定您正在处理的水果是苹果还是橙子,并以不同的方式处理它。
然后您想添加新班级,普通话。普通话应该像橙子一样,但在幕后却有着完全不同的实现。
您的每个动态演员都将您的普通话检测为非橙色。
另一方面,如果你有一个
virtual bool IsOrange() const = 0;
在你的界面中,并用它来检测橙子而不是动态转换,然后普通话可以自由地假装是橙子而不是真正的橙子。
这要求您的界面实际上足以解决您的问题。
顺便说一句,您使用的网站是由不擅长 C++ 的人编写的,请考虑使用其他资源。