前言
环境:UE 5.4.2
最近在写 UE C++ 代码的时候,突然思考了一个问题,为什么写 UE 可以直接用 Super 直接就可以拿到父类的 Class,我记得之前写纯C++的时候,如果想调用父类的函数,那得这样写
class Base {
public:virtual void foo() {std::cout << "Base foo" << std::endl;}
};class Derived : public Base {
public:void foo() override {std::cout << "Derived foo" << std::endl;// 调用父类的 fooBase::foo();}
};int main() {Derived d;d.foo(); // 将会输出 "Derived foo" 然后是 "Base foo"return 0;
}
问题引出
但是如果是 UE C++ 的话,可以这样写
UCLASS()
class Base : public UObject{
public:GENERATED_BODY()virtual void foo() {std::cout << "Base foo" << std::endl;}
};UCLASS()
class Derived : public Base {
public:void foo() override {std::cout << "Derived foo" << std::endl;// 调用父类的 fooSuper::foo(); // 这里不一样}
};
区别就是 UE C++ 可以不用直接知道父类的名字,而直接调用 Super 就可以了。
所以为什么 UE C++ 就和纯 C++ 不一样?难道 C++ 对 UE 有特殊照顾?😀
问题分析
这引起了我浓厚的一泡兴趣,而且正好有一丢丢时间,花点时间看看。
一、搜索 Super
首先肯定就直接搜索 Super 关键字了,看看 UE 到底做了啥。
最终我定位到了这里,下面的截图是:UnrealEngine\Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectMacros.h 文件下的。大家有源码环境可以自己定位到这里。
#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI ) \
private: \TClass& operator=(TClass&&); \TClass& operator=(const TClass&); \TRequiredAPI static UClass* GetPrivateStaticClass(); \
public: \/** Bitwise union of #EClassFlags pertaining to this class.*/ \static constexpr EClassFlags StaticClassFlags=EClassFlags(TStaticFlags); \/** Typedef for the base class ({{ typedef-type }}) */ \typedef TSuperClass Super;\/** Typedef for {{ typedef-type }}. */ \typedef TClass ThisClass;\/** Returns a UClass object representing this class at runtime */ \inline static UClass* StaticClass() \{ \return GetPrivateStaticClass(); \} \/** Returns the package this class belongs in */ \inline static const TCHAR* StaticPackage() \{ \return TPackage; \} \/** Returns the static cast flags for this class */ \inline static EClassCastFlags StaticClassCastFlags() \{ \return TStaticCastFlags; \} \/** For internal use only; use StaticConstructObject() to create new objects. */ \inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \{ \return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \} \/** For internal use only; use StaticConstructObject() to create new objects. */ \inline void* operator new( const size_t InSize, EInternal* InMem ) \{ \return (void*)InMem; \} \/* Eliminate V1062 warning from PVS-Studio while keeping MSVC and Clang happy. */ \inline void operator delete(void* InMem) \{ \::operator delete(InMem); \}
二、分析 DECLARE_CLASS 宏
根据上面的截图我们定位到了是有一个 typedef TSuperClass Super,我们才可以直接使用 Super。
那么 TSuperClass 又是啥?我们发现是 DECLARE_CLASS 宏传进来的参数,那么我们再去分析这个宏。
这个宏我一看,发现大概率是在类定义的时候用到的,因为传进来的都是类相关的,我们来尝试搜索下。
三、UObject 定义
根据线索,招到 UObject 定义的地方,这里在:UnrealEngine\Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h。
发现在 UObject 定义的时候就用了上面的宏,但是这里的 TClass 和 TSuperClass 都是自己,那是因为 UObject 差不多是 UE 最上层的类型了。(为什么说差不多,因为它明显还有继承自 UObjectBaseUtility)
到这里,我们就能知道为什么可以使用 Super 了,主要还是宏的定义。
但是!(看下面标题)
四、为什么自己生成的继承 UObject 的类也可以用 Super?
是啊,为什么可以呢?我们自己又没在自己的类里面去使用 DECLARE_CLASS 宏,为什么也可以用 Super?(比如文章开头的例子)
好奇心害死人
仔细思考下,我们创建一个 UE 的类型,继承自 UObject,代码编辑器会给我们生成如下代码。(不清楚的也可以试试)
#pragma once#include "CoreMinimal.h"
#include "TestObject.generated.h"UCLASS()
class UTestObject : public UObject
{GENERATED_BODY()
};
这个时候得有一点 UE 的 UBT、UHT 的知识了
不明白的同学可以去搜索下对应的知识,简单来说就是 UE 的反射机制,可以在编译前提前生成代码文件,将一些类的信息存储起来,供反射使用。
我们现在点进 TestObject.generated.h 文件去看看究竟。
五、generated.h
点击文件之后,我们直接搜索:DECLARE_CLASS
发现还真在这里面
DECLARE_CLASS(UTestObject, UObject, COMPILED_IN_FLAGS(0), CASTCLASS_None, TEXT(“/Script/Test”), NO_API)
所以答案已经很明显了,我们做一个总结。
总结
- UEC++ 之所以可以使用 Super、ThisClass 等关键字。
根本原因:DECLARE_CLASS 宏 传参进来给到 TSuperClass,然后定义了 typedef TSuperClass Super。
所以我们只需要使用这个宏就可以了。
- 为什么 UObject 要主动使用宏,但是我们自定义的类不需要?
根本原因:由于 UE C++ 的反射机制,帮我们自动使用了宏,而不需要我们手动去使用。
又学到了,知识 UP UP!