【转载】【UE4 C++】 Cast 方法分析

2,062 阅读2分钟

原文链接:【UE4 C++】 Cast 方法分析 | 程序员阿Tu

UE 的 Cast 方法,用于类型安全地进行动态转换,确保目标指针类型指向一个有效且完整的对象。

在 UE 中经常会使用继承自 SceneComponent 的对象作为 ActorRootCompoent 的情况,因此就需要使用 Cast 在使用时进行转换,大概的语法如下:

class UXXComponent : public USceneComponent

UXXComponent* child = CreateDefaultSubobject<UXXComponent>(TEXT("Root"));
RootComponent = child;
UXXComponent* root = Cast<UXXComponent>(RootComponent);

if (root != nullptr) root->SomeLogic();

RootComponent 在需要调用的时候调用 Cast 转换为子类调用其逻辑。

乍一看这里的 Cast 将一个父类指针转换为子类指针,是很危险不正确的,但这里是可以进行转换的。

具体原因之后再分析,需要明确的是 Cast 只能将指针转换到其自身或父类类型,这一点可以通过 Cast 的实现逻辑来确定。

查看 Cast 方法的实现,模板定义了两个形参,To 指定返回类型,From 为需要转换的原类型,内部调用了 TCastImplDoCast 方法,对于 UObject 的子类之间的转换,通过 TGetCastType 的判定进入 UObjectToUObject 逻辑。

// Dynamically cast an object type-safely.
template <typename To, typename From>
FORCEINLINE To* Cast(From* Src)
{
    return TCastImpl<From, To>::DoCast(Src);
}

template <typename From, typename To>
struct TCastImpl<From, To, ECastType::UObjectToUObject>
{
    FORCEINLINE static To* DoCast( UObject* Src )
    {
        return Src && Src->IsA<To>() ? (To*)Src : nullptr;
    }
}

DoCast 在判空后,核心逻辑就是 IsA<T> 方法,这个方法存在于 UObject 的父类 UObjectBaseUtility ,用于判断 Src 是否属于 To 类型,属于则直接进行强转。

继续看 IsA<T> 是如何实现的,内部进行了转换,传入了 T 类型的 UClass ,在内部使用 UClass 调用 IsChildOf 进行比较。

template<class T>
bool IsA() const
{
    return IsA(T::StaticClass());
}

const UClass* SomeBaseClass = SomeBase;
const UClass* ThisClass = GetClass();

return IsChildOfWorkaround(ThisClass, SomeBaseClass);

template <typename ClassType>
static FORCEINLINE bool IsChildOfWorkaround(const ClassType* ObjClass, const ClassType* TestCls)
{
    return ObjClass->IsChildOf(TestCls);
}

IsChildOf 内部则根据 UStruct 的父类标记,逐级向上遍历查询是否满足要求。

RootComponet 虽然静态类型为 USceneComponent实际上动态类型是其子类,通过 GetClass() 获取其 UClass 也能确定,因此 Cast在这里是同级的转换。

UClass* child = UXXRootComponent::StaticClass();
UClass* parent = USceneComponent::StaticClass();

UClass* root = RootComponent->GetClass();

UE_LOG(LogTemp, Warning, TEXT("%d"), child == root); // 输出:1
UE_LOG(LogTemp, Warning, TEXT("%d"), parent == root); //输出:0