UE5引擎源码小记 —反射信息注册过程

最近看了看反射相关的知识,用不说一点人话的方式来说,反射是程序在运行中能够动态获取修改或调用自身属性的东西。
一开始我是觉得反射用处好像不大,后续查了下一些反射的使用环境,发现我格局小了,我觉得用处不大的原因是我从c++的角度来考虑使用反射,在c++中确实用处不大,但是捏,把格局打开,要从更上一层的视角来看反射,不要局限于c++,比如我们要使用c++以外的东西调用c++,比如配置文件,蓝图,python,lua这些去调c++,或是设计到编辑器功能的时候,这时候反射就是极为好用方便的一个特性。

三种反射实现的方式

反射实现的本质其实就是想办法在运行时拿到类的数据,这个数据可以手动生成也可以利用编译器的信息,在c++中反射大概有三种实现方式
1.利用c++手动写反射结构的代码,可以利用宏之类的,其实就是方式3的简化版,实现简单相对简单,但是对使用者这来说毕竟麻烦,因为如果要添加一个新的成员或函数,就要手动添加一次。
2.利用c++编译出来的符号表,缺点每个编译器的实现不一定一样,换一个编译器可能就不好使了
3.利用工具生成c++代码实现反射,唯一缺点是实现起来比较有难度,但是使用起来简单,也不会有第二种方法的缺点

在ue中使用的就是第三种办法,所以ue实现了UHT这个工具。

UE5中反射系统注册信息的过程

UHT生成xxx.generated.h和xxx.gen.cpp反射信息文件
利用static变量的初始化预注册
注册完成的反射信息

UHT与宏

在编写ue代码时,相比大家一定遇到过类似于UCLASS,GENERATED_BODY,UFUNCTION这些宏,这些宏其实就是给UHT的一个标记,UHT更具这些宏生成generated.h和gen.cpp文件,这些宏在ObjectMarcos.h文件中可以找到详细的定义。现在我们来一步一步拨开这些宏背后所做的事情

比如,现在我们有这样一个AGhost类

UCLASS(config = Ghost)
class LEARNUE_API AGhost : public AActor, public FTickableGameObject
{GENERATED_BODY()
public:virtual void BeginPlay() override;virtual void Tick(float DeltaTime) override;AGhost();virtual  ~AGhost() override;UFUNCTION(BlueprintCallable)void PtLog(int i);public:UPROPERTY(VisibleAnywhere)int32 m_time ;
};

UCLASS

我们首先来看UCLASS宏的定义

#if UE_BUILD_DOCS || defined(__INTELLISENSE__ )
#define UCLASS(...)
#else
#define UCLASS(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)
#endif#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)

可以发现UCLASS这个宏仅仅是将三个字符串拼接起来。CURRENT_FILE_ID代表一个唯一的文件id,LINE为当前行号。
最后这个UCLASS会被替换成空行,我猜想是UCLASS宏内的参数在UHT运行的过程中被替换成flag之类的东西,所以这后续编译时候就没用了,

接着我们来看看UHT生成genrated.h文件可以发现这样的一个宏

#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID FID_UeProject_learnUe_Source_learnUe_Ghost_h#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_13_PROLOG

发现确实在最后UCLASS这个宏会被替换成空行。

GENERATED_BODY

GENERATED_BODY宏和UCLASS宏类似,也是拼接事先定义好内容,然后进行宏替换,不过GENERATED_BODY宏有着实际的替换内容。

#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);

然后GENERATED_BODY宏实际定义的东西在generated.h文件中

#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_SPARSE_DATA \FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_RPC_WRAPPERS_NO_PURE_DECLS \FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_ACCESSORS \FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_INCLASS_NO_PURE_DECLS \FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_ENHANCED_CONSTRUCTORS \
private: \

发现是宏套宏的套娃,不过被急,还有好多层套者呢。如果不想细看的朋友只要知道这些东西就是UHT帮助生成的代码。
SPARSE_DATA为稀疏数据的定义,WRAPPERS_NO_PURE_DECLS为UFunction的定义,ACCESSORS这个目前不太确定是啥,
INCLASS_NO_PURE_DECLS为序列化,StaticClass函数,Flag等一些的定义,DECLARE_VTABLE_PTR_HELPER_CTOR为默认构造函数,Vtable的定义。接下来我们一个一个拆这看。

RPC_WRAPPERS_NO_PURE_DECLS

#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_RPC_WRAPPERS \\DECLARE_FUNCTION(execPtLog);

我们同样可以在generated.h文件中找到这个宏的定义,发现是又套娃了一层DECLARE_FUNCTION的宏,宏的参数就是UFNCTION函数名,接着看看DECLARE_FUNCTION这个宏又做了什么。

#define DECLARE_FUNCTION(func) static void func( UObject* Context, FFrame& Stack, RESULT_DECL )

可以发现UFUNCTION宏会被UHT生成一个类内静态函数,这个类内静态函数的实现在对应的gen.cpp文件中

DEFINE_FUNCTION(func) void func( UObject* Context, FFrame& Stack, RESULT_DECL )DEFINE_FUNCTION(AGhost::execPtLog){P_GET_PROPERTY(FIntProperty,Z_Param_i);P_FINISH;P_NATIVE_BEGIN;P_THIS->PtLog(Z_Param_i);P_NATIVE_END;}

把这段全是宏的代码展开

void AGhost::execPtLog( UObject* Context, FFrame& Stack, RESULT_DECL )
{FIntProperty::TCppType Z_Param_i = PropertyType::GetDefaultPropertyValue();Stack.StepCompiledIn<FIntProperty>(&Z_Param_i);Stack.Code += !!Stack.Code;((AGhost*)(Context))->PtLog(Z_Param_i);
}

Context其实就是执行AGhost实体,参数类型为UObject*,但是实际上是AGhost*,这里利用的UObject做类型擦除,就和void*一样。 Stack为参数,比较好理解。

这个函数会由对应的UFunction::Invoke函数调用,完成反射函数的调用。

ACCESSORS

暂时不知道是做什么,pass

INCLASS_NO_PURE_DECLS

#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_INCLASS_NO_PURE_DECLS \
private: \static void StaticRegisterNativesAGhost(); \friend struct Z_Construct_UClass_AGhost_Statics; \
public: \DECLARE_CLASS(AGhost, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/learnUe"), NO_API) \DECLARE_SERIALIZER(AGhost) \static const TCHAR* StaticConfigName() {return TEXT("Ghost");} \

StaticRegisterNativesAGhost()函数的作用是向自己对应的Uclass类注册UFUNCTION,这个函数的定义在对应的gen.cpp中

void AGhost::StaticRegisterNativesAGhost(){UClass* Class = AGhost::StaticClass();static const FNameNativePtrPair Funcs[] = {{ "PtLog", &AGhost::execPtLog },};FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));}

DECLARE_CLASS宏则是定义一些UE类必有的内容,如StaticClass,StaticPackage,new,delete等必要的东西。
DECLARE_SERIALIZER宏看名字就很容易猜出来,必然是与序列化相关

#define DECLARE_SERIALIZER( TClass ) \friend FArchive &operator<<( FArchive& Ar, TClass*& Res ) \{ \return Ar << (UObject*&)Res; \} \friend void operator<<(FStructuredArchive::FSlot InSlot, TClass*& Res) \{ \InSlot << (UObject*&)Res; \}

STANDARD_CONSTRUCTORS

#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_ENHANCED_CONSTRUCTORS \
private: \/** Private move- and copy-constructors, should never be used */ \NO_API AGhost(AGhost&&); \NO_API AGhost(const AGhost&); \
public: \DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AGhost); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AGhost); \DEFINE_DEFAULT_CONSTRUCTOR_CALL(AGhost)

STANDARD_CONSTRUCTORS宏禁止了移动和拷贝,并定义了DEAFULT_CONSTRUCTOR,需要注意的是,这个并不是定义了默认的构造函数

#define DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }

到此为止.h和gen.h文件中的宏就展开完毕了。

反射信息注册

ue中反射信息是分段注册的,由static结构体的构造为第一段,执行在main函数之前,第二段在ue启动时完成真正的注册。
反射的信息几乎都在对应类的gen.cpp文件中,这也是由UHT生成的。

在gen.cpp文件中ue会生成一个每个类唯一的FClassRegistrationInfo变量,这个变量与GetPrivateStaticClass函数定义在gen.cpp文件中的IMPLEMENT_CLASS_NO_AUTO_REGISTRATION宏中,这个变量就包含了类的信息。

FClassRegistrationInfo

IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AGhost);#define IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(TClass) \FClassRegistrationInfo Z_Registration_Info_UClass_##TClass; \UClass* TClass::GetPrivateStaticClass() \{ \if (!Z_Registration_Info_UClass_##TClass.InnerSingleton) \{ \/* this could be handled with templates, but we want it external to avoid code bloat */ \GetPrivateStaticClassBody( \StaticPackage(), \(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \Z_Registration_Info_UClass_##TClass.InnerSingleton, \StaticRegisterNatives##TClass, \sizeof(TClass), \alignof(TClass), \(EClassFlags)TClass::StaticClassFlags, \TClass::StaticClassCastFlags(), \TClass::StaticConfigName(), \(UClass::ClassConstructorType)InternalConstructor<TClass>, \(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \UOBJECT_CPPCLASS_STATICFUNCTIONS_FORCLASS(TClass), \&TClass::Super::StaticClass, \&TClass::WithinClass::StaticClass \); \} \return Z_Registration_Info_UClass_##TClass.InnerSingleton; \}

IMPLEMENT_CLASS_NO_AUTO_REGISTRATION宏中定义了FClassRegistrationInfo类的对象和GetPrivateStaticClass函数,GetPrivateStaticClass函数可能大家不熟,但是StaticClass大家一定知道,其实StaticClass内部调用的就是GetPrivateStaticClass函数。
GetPrivateStaticClassBody的本质是在第一次调用时通过GetPrivateStaticClassBody函数生成一个uclass给Z_Registration_Info_UClass_##TClass.InnerSingleton参数。

GetPrivateStaticClassBody函数是比较长的,但是也比较简单

void GetPrivateStaticClassBody(const TCHAR* PackageName,const TCHAR* Name,UClass*& ReturnClass,void(*RegisterNativeFunc)(),uint32 InSize,uint32 InAlignment,EClassFlags InClassFlags,EClassCastFlags InClassCastFlags,const TCHAR* InConfigName,UClass::ClassConstructorType InClassConstructor,UClass::ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller,FUObjectCppClassStaticFunctions&& InCppClassStaticFunctions,UClass::StaticClassFunctionType InSuperClassFn,UClass::StaticClassFunctionType InWithinClassFn)
{
#if WITH_RELOADif (IsReloadActive() && GetActiveReloadType() != EActiveReloadType::Reinstancing){UPackage* Package = FindPackage(NULL, PackageName);if (Package){ReturnClass = FindObject<UClass>((UObject *)Package, Name);if (ReturnClass){if (ReturnClass->HotReloadPrivateStaticClass(InSize,InClassFlags,InClassCastFlags,InConfigName,InClassConstructor,InClassVTableHelperCtorCaller,FUObjectCppClassStaticFunctions(InCppClassStaticFunctions),InSuperClassFn(),InWithinClassFn())){// Register the class's native functions.RegisterNativeFunc();}return;}else{UE_LOG(LogClass, Log, TEXT("Could not find existing class %s in package %s for reload, assuming new or modified class"), Name, PackageName);}}else{UE_LOG(LogClass, Log, TEXT("Could not find existing package %s for reload of class %s, assuming a new package."), PackageName, Name);}}
#endifReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), alignof(UClass), true);ReturnClass = ::new (ReturnClass)UClass(EC_StaticConstructor,Name,InSize,InAlignment,InClassFlags,InClassCastFlags,InConfigName,EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet),InClassConstructor,InClassVTableHelperCtorCaller,MoveTemp(InCppClassStaticFunctions));check(ReturnClass);InitializePrivateStaticClass(InSuperClassFn(),ReturnClass,InWithinClassFn(),PackageName,Name);// Register the class's native functions.RegisterNativeFunc();
}

简单来说就是如果是热重载则执行HotReloadPrivateStaticClass,如果不是则new一个uclass类型出来,然后如果有继承关系则设置子父类的关系,最后调用参数RegisterNativeFunc函数。RegisterNativeFunc为gen.cpp文件中的StaticRegisterNatives开头加类名的函数,作用是注册类内拥有UFUNCTION宏的函数,

	void AGhost::StaticRegisterNativesAGhost(){UClass* Class = AGhost::StaticClass();static const FNameNativePtrPair Funcs[] = {{ "PtLog", &AGhost::execPtLog },};FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));}

利用static变量

ue利用static变量的初始化进行预注册,预注册指的是仅收集谁是要注册的类,不进行真正的注册,真正的注册等到程序起来后在进行,我猜测ue不在static初始化时就注册完全,是因为如果都在static初始化时注册程序启动时耗费时间比较长,会被误以为卡死之类的。

接下来我们来看ue是怎么利用static变量的初始化进行注册的。在gen.cpp文件中有着这么一个静态变量

	static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_3523060098(TEXT("/Script/learnUe"),Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo),nullptr, 0,nullptr, 0);

FRegisterCompiledInInfo类型就是ue封装好的一个,我们看下它的本质

struct FRegisterCompiledInInfo
{template <typename ... Args>FRegisterCompiledInInfo(Args&& ... args){RegisterCompiledInInfo(std::forward<Args>(args)...);}
};void RegisterCompiledInInfo(class UClass* (*InOuterRegister)(), class UClass* (*InInnerRegister)(), const TCHAR* InPackageName, const TCHAR* InName, FClassRegistrationInfo& InInfo, const FClassReloadVersionInfo& InVersionInfo)
{check(InOuterRegister);check(InInnerRegister);FClassDeferredRegistry::AddResult result = FClassDeferredRegistry::Get().AddRegistration(InOuterRegister, InInnerRegister, InPackageName, InName, InInfo, InVersionInfo);
#if WITH_RELOADif (result == FClassDeferredRegistry::AddResult::ExistingChanged && !IsReloadActive()){// Class exists, this can only happen during hot-reload or live codingUE_LOG(LogUObjectBase, Fatal, TEXT("Trying to recreate changed class '%s' outside of hot reload and live coding!"), InName);}
#endifFString NoPrefix(UObjectBase::RemoveClassPrefix(InName));NotifyRegistrationEvent(InPackageName, *NoPrefix, ENotifyRegistrationType::NRT_Class, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);NotifyRegistrationEvent(InPackageName, *(FString(DEFAULT_OBJECT_PREFIX) + NoPrefix), ENotifyRegistrationType::NRT_ClassCDO, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);
}

在构造时候则执行RegisterCompiledInInfo函数,顺带转发参数。顺带一提,RegisterCompiledInInfo函数有多个重载,我这里仅展示UCLASS的重载,实际上UEnum,UStruc调用的都是不同的RegisterCompiledInInfo函数,
FStructDeferredRegistry可以简单理解成一个模板数组,数组里存的是AddRegistration函数的参数信息。虽然实际上FStructDeferredRegistry内部就是数组结构,但是内部信息做了一定的封装。在ue中class,enum,stuct和package都有自己的DeferredRegistry,如FClassDeferredRegistry的声明处。

using FClassDeferredRegistry = TDeferredRegistry<FClassRegistrationInfo>;
using FEnumDeferredRegistry = TDeferredRegistry<FEnumRegistrationInfo>;
using FStructDeferredRegistry = TDeferredRegistry<FStructRegistrationInfo>;
using FPackageDeferredRegistry = TDeferredRegistry<FPackageRegistrationInfo>;

接着AddRegistration函数和NotifyRegistrationEvent分别注册了InOuterRegister的函数指针和InInnerRegister函数指针,这个两个函数指针实际上也是gen.cpp文件中生成的函数。分别为Z_Construct_UClass+类名的函数和StaticClass函数,不过这里先放一放。我们先看AddRegistration函数做了什么。

/// <summary>/// Adds the given registration information for the given object.  Objects are either classes, structs, or enumerations./// </summary>/// <param name="InOuterRegister">Returns a fully initialize instance of the object</param>/// <param name="InInnerRegister">Returns an allocated but uninitialized instance of the object.  This is used only by UClass.</param>/// <param name="InPackageName">Name of the package</param>/// <param name="InName">Name of the object</param>/// <param name="InInfo">Persistent information about the object</param>/// <param name="InVersion">Version information for this incarnation of the object</param>AddResult AddRegistration(TType* (*InOuterRegister)(), TType* (*InInnerRegister)(), const TCHAR* InPackageName, const TCHAR* InName, TInfo& InInfo, const TVersion& InVersion){
#if WITH_RELOADconst FPackageAndNameKey Key = FPackageAndNameKey{ InPackageName, InName };TInfo** ExistingInfo = InfoMap.Find(Key);bool bHasChanged = !ExistingInfo || (*ExistingInfo)->ReloadVersionInfo != InVersion;InInfo.ReloadVersionInfo = InVersion;TType* OldSingleton = ExistingInfo ? ((*ExistingInfo)->InnerSingleton ? (*ExistingInfo)->InnerSingleton : (*ExistingInfo)->OuterSingleton) : nullptr;bool bAdd = true;if (ExistingInfo){if (IReload* Reload = GetActiveReloadInterface()){bAdd = Reload->GetEnableReinstancing(bHasChanged);}if (bAdd){if (!bHasChanged){// With live coding, the existing might be the same as the new info.  // We still invoke the copy method to allow UClasses to clear the singletons.UpdateSingletons(InInfo, **ExistingInfo);}*ExistingInfo = &InInfo;}}else{InfoMap.Add(Key, &InInfo);}if (bAdd){Registrations.Add(FRegistrant{ InOuterRegister, InInnerRegister, InPackageName, &InInfo, OldSingleton, bHasChanged });}return ExistingInfo == nullptr ? AddResult::New : (bHasChanged ? AddResult::ExistingChanged : AddResult::ExistingNoChange);
#elseRegistrations.Add(FRegistrant{ InOuterRegister, InInnerRegister, InPackageName, &InInfo });return AddResult::New;
#endif}

AddRegistration函数的逻辑是现在自身map上找一下是否存在要add的类,如果有则看下是否为热重载,如果没有则加入map,最后在把信息包装成FRegistrant放入自身的Registrations数组中,待后续使用。
然后回到RegisterCompiledInInfo函数,在AddRegistration后又调用了NotifyRegistrationEvent函数,看名字是通知什么模块有注册事件。

TUniquePtr<IAsyncPackageLoader> GPackageLoader;void NotifyRegistrationEvent(const TCHAR* PackageName, const TCHAR* Name, ENotifyRegistrationType NotifyRegistrationType, ENotifyRegistrationPhase NotifyRegistrationPhase, UObject *(*InRegister)(), bool InbDynamic, UObject* FinishedObject)
{LLM_SCOPE(ELLMTag::AsyncLoading);if (GPackageLoader){GPackageLoader->NotifyRegistrationEvent(PackageName, Name, NotifyRegistrationType, NotifyRegistrationPhase, InRegister, InbDynamic, FinishedObject);}else{GetEarlyRegistrationEventsRecorder().NotifyRegistrationEvent(PackageName, Name, NotifyRegistrationType, NotifyRegistrationPhase, InRegister, InbDynamic, FinishedObject);}
}

值得注意的是这里GPackageLoader的类型,声明上是IAsyncPackageLoader,但是实际上是FAsyncLoadingThread,这两个类是父子关系,函数为虚函数,c++里很经典的一种用法,所以GPackageLoader->NotifyRegistrationEvent调用的实际上是FAsyncLoadingThread->NotifyRegistrationEvent,来看下这个函数做了什么

struct FEDLBootNotificationManager& EDLBootNotificationManager;void FAsyncLoadingThread::NotifyRegistrationEvent(const TCHAR* PackageName, const TCHAR* Name, ENotifyRegistrationType NotifyRegistrationType, ENotifyRegistrationPhase NotifyRegistrationPhase, UObject* (*InRegister)(), bool InbDynamic, UObject* FinishedObject)
{EDLBootNotificationManager.NotifyRegistrationEvent(PackageName, Name, NotifyRegistrationType, NotifyRegistrationPhase, InRegister, InbDynamic);
}

emmm看这变量名字,是这EDLBoot又代表啥缩写,晕,查了下大概一个ue启动时辅助初始化的东东。暂时不太明白作用。

inner注册

我们知道了ue利用static变量在main函数之前注册了一些信息,现在我来看看ue是如使用这些DeferredRegistry 变量的。

在ue启动时,大概的时间点是在loadModule时,会执行一个一个叫做UClassRegisterAllCompiledInClasses的函数。

void UClassRegisterAllCompiledInClasses()
{
#if WITH_RELOADTArray<UClass*> AddedClasses;
#endifSCOPED_BOOT_TIMING("UClassRegisterAllCompiledInClasses");FClassDeferredRegistry& Registry = FClassDeferredRegistry::Get();Registry.ProcessChangedObjects();for (const FClassDeferredRegistry::FRegistrant& Registrant : Registry.GetRegistrations()){UClass* RegisteredClass = FClassDeferredRegistry::InnerRegister(Registrant);
#if WITH_RELOADif (IsReloadActive() && Registrant.OldSingleton == nullptr){AddedClasses.Add(RegisteredClass);}
#endif}#if WITH_RELOADif (AddedClasses.Num() > 0){FCoreUObjectDelegates::ReloadAddedClassesDelegate.Broadcast(AddedClasses);PRAGMA_DISABLE_DEPRECATION_WARNINGSFCoreUObjectDelegates::RegisterHotReloadAddedClassesDelegate.Broadcast(AddedClasses);PRAGMA_ENABLE_DEPRECATION_WARNINGS}
#endif
}

FClassDeferredRegistry::Get()其实就是获取单例,然后将之前利用static变量注册进去的类,执行InnerRegiste函数指针,这个InnerRegiste是之前gen.cpp文件中的那个StaticClass函数,其内部就是我们一开始说的GetPrivateStaticClass函数。它实例化出了uclass类并赋予信息,但是到这里还没有完,到这里uclass仅仅只有一些ue对类的附加信息,如flag,configname等,类内成员和类内函数的信息没有注册到uclass,如果细心一点会注意到,gen。cpp文件中不仅有inner,还有outer,
后面的outer就是注册类内成员和类内成员函数的

outer注册

在gen.cpp的文件中有着这样的一个函数,它名字是Z_Construct_UClass_+类名,
在我的AGhost类中它为Z_Construct_UClass_AGhost

UClass* Z_Construct_UClass_AGhost(){if (!Z_Registration_Info_UClass_AGhost.OuterSingleton){UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AGhost.OuterSingleton, Z_Construct_UClass_AGhost_Statics::ClassParams);}return Z_Registration_Info_UClass_AGhost.OuterSingleton;}

内部使用了UECodeGen_Private::ConstructUClass进行构造outer,参数呢是熟悉的Z_Registration_Info_UClass_AGhost,就是那个staticclass函数里用的那个。同时也是和inner一样只需要构建一次。
我们接下来看看这个ConstructUClass做了什么

void ConstructUClass(UClass*& OutClass, const FClassParams& Params){if (OutClass && (OutClass->ClassFlags & CLASS_Constructed)){return;}for (UObject* (*const *SingletonFunc)() = Params.DependencySingletonFuncArray, *(*const *SingletonFuncEnd)() = SingletonFunc + Params.NumDependencySingletons; SingletonFunc != SingletonFuncEnd; ++SingletonFunc){(*SingletonFunc)();}UClass* NewClass = Params.ClassNoRegisterFunc();OutClass = NewClass;if (NewClass->ClassFlags & CLASS_Constructed){return;}UObjectForceRegistration(NewClass);UClass* SuperClass = NewClass->GetSuperClass();if (SuperClass){NewClass->ClassFlags |= (SuperClass->ClassFlags & CLASS_Inherit);}NewClass->ClassFlags |= (EClassFlags)(Params.ClassFlags | CLASS_Constructed);// Make sure the reference token stream is empty since it will be reconstructed later on// This should not apply to intrinsic classes since they emit native references before AssembleReferenceTokenStream is called.if ((NewClass->ClassFlags & CLASS_Intrinsic) != CLASS_Intrinsic){check((NewClass->ClassFlags & CLASS_TokenStreamAssembled) != CLASS_TokenStreamAssembled);NewClass->ReferenceTokenStream.Empty();}NewClass->CreateLinkAndAddChildFunctionsToMap(Params.FunctionLinkArray, Params.NumFunctions);ConstructFProperties(NewClass, Params.PropertyArray, Params.NumProperties);if (Params.ClassConfigNameUTF8){NewClass->ClassConfigName = FName(UTF8_TO_TCHAR(Params.ClassConfigNameUTF8));}NewClass->SetCppTypeInfoStatic(Params.CppClassInfo);if (int32 NumImplementedInterfaces = Params.NumImplementedInterfaces){NewClass->Interfaces.Reserve(NumImplementedInterfaces);for (const FImplementedInterfaceParams* ImplementedInterface = Params.ImplementedInterfaceArray, *ImplementedInterfaceEnd = ImplementedInterface + NumImplementedInterfaces; ImplementedInterface != ImplementedInterfaceEnd; ++ImplementedInterface){UClass* (*ClassFunc)() = ImplementedInterface->ClassFunc;UClass* InterfaceClass = ClassFunc ? ClassFunc() : nullptr;NewClass->Interfaces.Emplace(InterfaceClass, ImplementedInterface->Offset, ImplementedInterface->bImplementedByK2);}}#if WITH_METADATAAddMetaData(NewClass, Params.MetaDataArray, Params.NumMetaData);
#endifNewClass->StaticLink();NewClass->SetSparseClassDataStruct(NewClass->GetSparseClassDataArchetypeStruct());}
}

可以看到首先执行了parm.DependencySingletonFuncArray参数中所有的函数指针,而这个parm在gen.cpp文件是个叫ClassParams的FClassParmas的结构体,里面存放着UFuntion的信息。

const UECodeGen_Private::FClassParams Z_Construct_UClass_AGhost_Statics::ClassParams = {&AGhost::StaticClass,"Ghost",&StaticCppClassTypeInfo,DependentSingletons,FuncInfo,Z_Construct_UClass_AGhost_Statics::PropPointers,nullptr,UE_ARRAY_COUNT(DependentSingletons),UE_ARRAY_COUNT(FuncInfo),UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::PropPointers),0,0x009000A4u,METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams))};//FclassParms定义struct FClassParams{UClass*                                   (*ClassNoRegisterFunc)();const char*                                 ClassConfigNameUTF8;const FCppClassTypeInfoStatic*              CppClassInfo;UObject*                           (*const *DependencySingletonFuncArray)();const FClassFunctionLinkInfo*               FunctionLinkArray;const FPropertyParamsBase* const*           PropertyArray;const FImplementedInterfaceParams*          ImplementedInterfaceArray;int32                                       NumDependencySingletons;int32                                       NumFunctions;int32                                       NumProperties;int32                                       NumImplementedInterfaces;uint32                                      ClassFlags; // EClassFlags
#if WITH_METADATAconst FMetaDataPairParam*                   MetaDataArray;int32                                       NumMetaData;
#endif};

ConstructUClass函数主要就是利用FClassParams去构建Uclass的信息,如果全部展开篇幅实在是太太长了,我也写不动啦,想摸了。有兴趣可以自行找下生成的gen.cpp文件,基本都在那里啦。

副案例代码

//```cpp
//Ghost.gen.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
/*===========================================================================Generated code exported from UnrealHeaderTool.DO NOT modify this manually! Edit the corresponding .h files instead!
===========================================================================*/#include "UObject/GeneratedCppIncludes.h"
#include "learnUe/Ghost.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeGhost() {}
// Cross Module ReferencesENGINE_API UClass* Z_Construct_UClass_AActor();LEARNUE_API UClass* Z_Construct_UClass_AGhost();LEARNUE_API UClass* Z_Construct_UClass_AGhost_NoRegister();UPackage* Z_Construct_UPackage__Script_learnUe();
// End Cross Module ReferencesDEFINE_FUNCTION(AGhost::execPtLog){P_GET_PROPERTY(FIntProperty,Z_Param_i);P_FINISH;P_NATIVE_BEGIN;P_THIS->PtLog(Z_Param_i);P_NATIVE_END;}void AGhost::StaticRegisterNativesAGhost(){UClass* Class = AGhost::StaticClass();static const FNameNativePtrPair Funcs[] = {{ "PtLog", &AGhost::execPtLog },};FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));}struct Z_Construct_UFunction_AGhost_PtLog_Statics{struct Ghost_eventPtLog_Parms{int32 i;};static const UECodeGen_Private::FUnsizedIntPropertyParams NewProp_i;static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endifstatic const UECodeGen_Private::FFunctionParams FuncParams;};const UECodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i = { "i", nullptr, (EPropertyFlags)0x0010000000000080, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(Ghost_eventPtLog_Parms, i), METADATA_PARAMS(nullptr, 0) };const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers[] = {(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i,};
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams[] = {{ "ModuleRelativePath", "Ghost.h" },};
#endifconst UECodeGen_Private::FFunctionParams Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams = { (UObject*(*)())Z_Construct_UClass_AGhost, nullptr, "PtLog", nullptr, nullptr, sizeof(Z_Construct_UFunction_AGhost_PtLog_Statics::Ghost_eventPtLog_Parms), Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers), RF_Public|RF_Transient|RF_MarkAsNative, (EFunctionFlags)0x04020401, 0, 0, METADATA_PARAMS(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams)) };UFunction* Z_Construct_UFunction_AGhost_PtLog(){static UFunction* ReturnFunction = nullptr;if (!ReturnFunction){UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams);}return ReturnFunction;}IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AGhost);UClass* Z_Construct_UClass_AGhost_NoRegister(){return AGhost::StaticClass();}struct Z_Construct_UClass_AGhost_Statics{static UObject* (*const DependentSingletons[])();static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam NewProp_m_time_MetaData[];
#endifstatic const UECodeGen_Private::FIntPropertyParams NewProp_m_time;static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;static const UECodeGen_Private::FClassParams ClassParams;};UObject* (*const Z_Construct_UClass_AGhost_Statics::DependentSingletons[])() = {(UObject* (*)())Z_Construct_UClass_AActor,(UObject* (*)())Z_Construct_UPackage__Script_learnUe,};const FClassFunctionLinkInfo Z_Construct_UClass_AGhost_Statics::FuncInfo[] = {{ &Z_Construct_UFunction_AGhost_PtLog, "PtLog" }, // 1286413536};
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams[] = {{ "IncludePath", "Ghost.h" },{ "ModuleRelativePath", "Ghost.h" },};
#endif
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData[] = {{ "Category", "Ghost" },{ "ModuleRelativePath", "Ghost.h" },};
#endifconst UECodeGen_Private::FIntPropertyParams Z_Construct_UClass_AGhost_Statics::NewProp_m_time = { "m_time", nullptr, (EPropertyFlags)0x0010000000020001, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(AGhost, m_time), METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData)) };const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AGhost_Statics::PropPointers[] = {(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AGhost_Statics::NewProp_m_time,};const FCppClassTypeInfoStatic Z_Construct_UClass_AGhost_Statics::StaticCppClassTypeInfo = {TCppClassTypeTraits<AGhost>::IsAbstract,};const UECodeGen_Private::FClassParams Z_Construct_UClass_AGhost_Statics::ClassParams = {&AGhost::StaticClass,"Ghost",&StaticCppClassTypeInfo,DependentSingletons,FuncInfo,Z_Construct_UClass_AGhost_Statics::PropPointers,nullptr,UE_ARRAY_COUNT(DependentSingletons),UE_ARRAY_COUNT(FuncInfo),UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::PropPointers),0,0x009000A4u,METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams))};UClass* Z_Construct_UClass_AGhost(){if (!Z_Registration_Info_UClass_AGhost.OuterSingleton){UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AGhost.OuterSingleton, Z_Construct_UClass_AGhost_Statics::ClassParams);}return Z_Registration_Info_UClass_AGhost.OuterSingleton;}template<> LEARNUE_API UClass* StaticClass<AGhost>(){return AGhost::StaticClass();}DEFINE_VTABLE_PTR_HELPER_CTOR(AGhost);struct Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics{static const FClassRegisterCompiledInInfo ClassInfo[];};const FClassRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo[] = {{ Z_Construct_UClass_AGhost, AGhost::StaticClass, TEXT("AGhost"), &Z_Registration_Info_UClass_AGhost, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AGhost), 1620684469U) },};static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_3523060098(TEXT("/Script/learnUe"),Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo),nullptr, 0,nullptr, 0);
PRAGMA_ENABLE_DEPRECATION_WARNINGS```Ghost.h
UCLASS(config = Ghost)
class LEARNUE_API AGhost : public AActor, public FTickableGameObject
{GENERATED_BODY()
public:virtual void BeginPlay() override;virtual void Tick(float DeltaTime) override;virtual TStatId GetStatId() const override{return Super::GetStatID();}public:AGhost();virtual  ~AGhost() override;UFUNCTION(BlueprintCallable)void PtLog(int i);public:FGhostDelegate m_ghostDelegate;UPROPERTY(VisibleAnywhere)int32 m_time ;
};
//Ghost.gen.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
/*===========================================================================Generated code exported from UnrealHeaderTool.DO NOT modify this manually! Edit the corresponding .h files instead!
===========================================================================*/#include "UObject/GeneratedCppIncludes.h"
#include "learnUe/Ghost.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeGhost() {}
// Cross Module ReferencesENGINE_API UClass* Z_Construct_UClass_AActor();LEARNUE_API UClass* Z_Construct_UClass_AGhost();LEARNUE_API UClass* Z_Construct_UClass_AGhost_NoRegister();UPackage* Z_Construct_UPackage__Script_learnUe();
// End Cross Module ReferencesDEFINE_FUNCTION(AGhost::execPtLog){P_GET_PROPERTY(FIntProperty,Z_Param_i);P_FINISH;P_NATIVE_BEGIN;P_THIS->PtLog(Z_Param_i);P_NATIVE_END;}void AGhost::StaticRegisterNativesAGhost(){UClass* Class = AGhost::StaticClass();static const FNameNativePtrPair Funcs[] = {{ "PtLog", &AGhost::execPtLog },};FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));}struct Z_Construct_UFunction_AGhost_PtLog_Statics{struct Ghost_eventPtLog_Parms{int32 i;};static const UECodeGen_Private::FUnsizedIntPropertyParams NewProp_i;static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endifstatic const UECodeGen_Private::FFunctionParams FuncParams;};const UECodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i = { "i", nullptr, (EPropertyFlags)0x0010000000000080, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(Ghost_eventPtLog_Parms, i), METADATA_PARAMS(nullptr, 0) };const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers[] = {(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i,};
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams[] = {{ "ModuleRelativePath", "Ghost.h" },};
#endifconst UECodeGen_Private::FFunctionParams Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams = { (UObject*(*)())Z_Construct_UClass_AGhost, nullptr, "PtLog", nullptr, nullptr, sizeof(Z_Construct_UFunction_AGhost_PtLog_Statics::Ghost_eventPtLog_Parms), Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers), RF_Public|RF_Transient|RF_MarkAsNative, (EFunctionFlags)0x04020401, 0, 0, METADATA_PARAMS(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams)) };UFunction* Z_Construct_UFunction_AGhost_PtLog(){static UFunction* ReturnFunction = nullptr;if (!ReturnFunction){UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams);}return ReturnFunction;}IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AGhost);UClass* Z_Construct_UClass_AGhost_NoRegister(){return AGhost::StaticClass();}struct Z_Construct_UClass_AGhost_Statics{static UObject* (*const DependentSingletons[])();static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam NewProp_m_time_MetaData[];
#endifstatic const UECodeGen_Private::FIntPropertyParams NewProp_m_time;static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;static const UECodeGen_Private::FClassParams ClassParams;};UObject* (*const Z_Construct_UClass_AGhost_Statics::DependentSingletons[])() = {(UObject* (*)())Z_Construct_UClass_AActor,(UObject* (*)())Z_Construct_UPackage__Script_learnUe,};const FClassFunctionLinkInfo Z_Construct_UClass_AGhost_Statics::FuncInfo[] = {{ &Z_Construct_UFunction_AGhost_PtLog, "PtLog" }, // 1286413536};
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams[] = {{ "IncludePath", "Ghost.h" },{ "ModuleRelativePath", "Ghost.h" },};
#endif
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData[] = {{ "Category", "Ghost" },{ "ModuleRelativePath", "Ghost.h" },};
#endifconst UECodeGen_Private::FIntPropertyParams Z_Construct_UClass_AGhost_Statics::NewProp_m_time = { "m_time", nullptr, (EPropertyFlags)0x0010000000020001, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(AGhost, m_time), METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData)) };const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AGhost_Statics::PropPointers[] = {(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AGhost_Statics::NewProp_m_time,};const FCppClassTypeInfoStatic Z_Construct_UClass_AGhost_Statics::StaticCppClassTypeInfo = {TCppClassTypeTraits<AGhost>::IsAbstract,};const UECodeGen_Private::FClassParams Z_Construct_UClass_AGhost_Statics::ClassParams = {&AGhost::StaticClass,"Ghost",&StaticCppClassTypeInfo,DependentSingletons,FuncInfo,Z_Construct_UClass_AGhost_Statics::PropPointers,nullptr,UE_ARRAY_COUNT(DependentSingletons),UE_ARRAY_COUNT(FuncInfo),UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::PropPointers),0,0x009000A4u,METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams))};UClass* Z_Construct_UClass_AGhost(){if (!Z_Registration_Info_UClass_AGhost.OuterSingleton){UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AGhost.OuterSingleton, Z_Construct_UClass_AGhost_Statics::ClassParams);}return Z_Registration_Info_UClass_AGhost.OuterSingleton;}template<> LEARNUE_API UClass* StaticClass<AGhost>(){return AGhost::StaticClass();}DEFINE_VTABLE_PTR_HELPER_CTOR(AGhost);struct Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics{static const FClassRegisterCompiledInInfo ClassInfo[];};const FClassRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo[] = {{ Z_Construct_UClass_AGhost, AGhost::StaticClass, TEXT("AGhost"), &Z_Registration_Info_UClass_AGhost, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AGhost), 1620684469U) },};static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_3523060098(TEXT("/Script/learnUe"),Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo),nullptr, 0,nullptr, 0);
PRAGMA_ENABLE_DEPRECATION_WARNINGS

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11408.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

43.240.75.X服务器是什么,有什么作用?

什么是服务器&#xff1f;今天我们来了解下服务器到底是什么&#xff1f;还有服务器用途是什么&#xff1f;让我们对服务器有一个简单的了解。 43.240.75.1什么是服务器&#xff1f; 服务器是一台专门用于存储、处理和传输数据的计算机设备。它在网络中扮演着重要角色&#x…

cnvd水证书

日常水个证书 本人比较菜&#xff0c;挖cnvd总是挖不倒漏洞&#xff0c;只能从弱口令入手了&#xff0c;一般使用fofa脚本批量跑。 过程&#xff1a; 日常在佛法中我们总是会遇见设备弱口或系统弱口令 一般是看登录成功的特征和口令&#xff0c;写一个对应的弱口令检测脚本…

Linux虚拟机(lvm)报Unmount and run xfs_repair

问题 linux系统没有正常关机&#xff0c;今天启动虚拟机无法进入系统&#xff0c;提示metadata corruption deleted at xxxx&#xff1b; Unmount and run xfs_repair 分析 主机异常掉电后里面的虚拟机无法启动&#xff0c;主要是损坏的分区 解决 看出来应该是dm-0分区损坏…

unity表情显示在android或者ios原生页面上

国际通用表情库&#xff0c;附带表情unicode码 在unity里面将unicode码转换成字符串&#xff0c;然后就可以再android和ios原生上显示了&#xff0c; 如下是在unity里面将unicode码转字符串方法&#xff1a; string unicodeString char.ConvertFromUtf32((int)emojiSprite.un…

kubernetes中常见的exited code总结

什么是容器退出码 当容器终止时&#xff0c;容器引擎使用退出码来报告容器终止的原因。如果是 Kubernetes 用户&#xff0c;容器故障是 pod 异常最常见的原因之一&#xff0c;了解常见的容器退出码可以帮助在排查时更快捷找到 pod异常的根本原因。可以参考https://komodor.com…

WebClient,HTTP Interface远程调用阿里云API

HTTP Interface Spring 允许我们通过定义接口的方式&#xff0c;给任意位置发送 http 请求&#xff0c;实现远程调用&#xff0c;可以用来简化 HTTP 远程访问。需要webflux场景才可 <dependency><groupId>org.springframework.boot</groupId><artifactId&…

Spring中最简单的过滤器和监听器

1. 过滤器概念引入 Filter也称之为过滤器&#xff0c;它是Servlet技术中最实用的技术&#xff0c;Web开发人员通过Filter技术&#xff0c;对web服务器管理的所有web资源&#xff1a;例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截&#xff0c;从而实现一些特殊的功…

uniapp 微信小程序:页面+组件的生命周期顺序

uniapp 微信小程序&#xff1a;页面组件的生命周期顺序 首页页面父组件子组件完整顺序参考资料 这个uniapp的微信小程序项目使用的是 VUE2 首页 首页只提供了一个跳转按钮。 <template><view><navigator url"/pages/myPage/myPage?namejerry" hov…

kafka服务端允许生产者发送最大消息体大小

1、kafka config服务端配置文件server.properties server.properties中加上的message.max.bytes配置&#xff0c;我目前设置为5242880&#xff0c;即5MB&#xff0c;可以根据实际情况增大。 message.max.bytes5242880 在生产者端配置max.request.size&#xff0c;这是单个消息…

【React】精选5题

第1题&#xff1a;简述下 React 的生命周期&#xff1f;每个生命周期都做了什么&#xff1f; React 组件的生命周期可以分为三个阶段&#xff1a;挂载阶段、更新阶段和卸载阶段。每个生命周期方法都有特定的目的和功能。 挂载阶段&#xff1a; constructor&#xff1a;组件的构…

protobuf入门实践2

如何在proto中定义一个rpc服务? syntax "proto3"; //声明protobuf的版本package fixbug; //声明了代码所在的包 &#xff08;对于C来说就是namespace)//下面的选项&#xff0c;表示生成service服务类和rpc方法描述&#xff0c; 默认是不生成的 option cc_generi…

如何正确使用npm常用命令

npm常用命令&#xff1a; 官方文档&#xff1a;CLI Commands | npm Docs 1. npm -v&#xff1a;查看 npm 版本 2. npm init&#xff1a;初始化后会出现一个 Package.json 配置文件&#xff0c;可以在后面加上 -y&#xff0c;快速跳到问答界面 3. npm install&#xff1a;会…

Iceberg 合并datafiles、manifest files,清除过期snapshot、删除孤立文件

本文Iceberg使用的为HiveCataLog&#xff0c;依赖HiveMemstore 1、首先获取要操作的表对象及SparkSession import org.apache.iceberg.{CatalogProperties, Table} import org.apache.iceberg.spark.actions.SparkActions ...... ...... ...... //获取表 val tabled: TableIden…

6个高清图片素材网站,免费下载,值得推荐~

关于图片素材网站&#xff0c;我一直都在推荐这几个&#xff0c;免费下载&#xff0c;可商用&#xff0c;建议收藏起来~ 菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 网站主要是为新手设计师提供免费素材的&#xff0c;素材的质量都很高&#xff0c;类别也很多&a…

在C++中 ,什么时候用:: ?什么时候用. ?什么时候用->?

在C中 &#xff0c;什么时候用:: ?什么时候用. ?什么时候用->?在 C 中&#xff0c;::、. 和 -> 是三种不同的运算符&#xff0c;用于访问类、结构体、命名空间、指针等的成员。它们的使用场景如下&#xff1a; ::&#xff08;作用域解析运算符&#xff09;&#xff1a…

Git移除commit过的大文件

前言&#xff1a;在提交推送本地更改至仓库时&#xff0c;误将大文件给提交了&#xff0c;导致push时报错文件过大&#xff0c;因此需要将已经commit的大文件移除后再push 若已知要删除的文件或文件夹路径&#xff0c;则可以从第4步开始 1.对仓库进行gc操作 $ git gc 2.查询…

23款奔驰S400豪华型升级后排电动腿托系统,提升后排乘坐舒适性

奔驰S400L后排座椅是不带腿托和脚托的&#xff0c;也没有一键躺平功能&#xff0c;相对于奔驰S级高配车型上配置的右边老板位座椅&#xff0c;舒适性就差强了一些。

【基于Spark的电影推荐系统】环境准备

概览 本科毕设做过电影推荐系统&#xff0c;但当时的推荐算法只有一个&#xff0c;现在已经忘记大部分了&#xff0c;当时也没有记录&#xff0c;因此写这个博客重新来记录一下。此外&#xff0c;技术栈由于快秋招原因来不及做过多的部分&#xff0c;因此只实现简单的功能&…

Vue.nextTick函数的用法及在异步更新中的应用

在Vue.js中&#xff0c;我们经常会遇到需要在DOM更新之后执行一些操作的情况。但是由于Vue的响应式更新是异步执行的&#xff0c;直接在更新数据后立即操作DOM可能得不到正确的结果。为了解决这个问题&#xff0c;Vue提供了Vue.nextTick函数。 Vue.nextTick函数是一个异步方法…

从零开始 Spring Cloud 7:Gateway

从零开始 Spring Cloud 7&#xff1a;Gateway 图源&#xff1a;laiketui.com Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨…