上一篇我们讲到了利用static变量把构造函数指针以及生成的类信息都收集到了全局静态数组中。这一篇就要讲讲,收集好了之后,我们是怎么利用这些收集的信息来生成我们的UClass的。
上一篇最后说到了,IMPLEMENT_VM_FUNCTION(EX_CallMath, execCallMathFunction)
会触发UObject::StaticClass()
的调用,这里会生成第一个UClass*。
那么UObject的IMPLEMENT_CLASS是在哪里定义的呢?就在NoexportTypes.h文件中。
这个文件定义了Eunm,Struct和UObject,但是不参与编译,只是提供给UHT来生成反射信息的相关代码以便于其他地方去调用。那我们接下来去看下StaticClass具体做了什么。
//类的声明值
DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), CASTCLASS_None, TEXT("/Script/Hello"), NO_API)
//值的传递
UClass* UMyClass::GetPrivateStaticClass(const TCHAR* Package)
{
static UClass* PrivateStaticClass = NULL; //静态变量,下回访问就不用再去查找了
if (!PrivateStaticClass)
{
/* this could be handled with templates, but we want it external to avoid code bloat */
GetPrivateStaticClassBody(
Package, //包名,TEXT("/Script/Hello"),用来把本UClass*构造在该UPackage里
(TCHAR*)TEXT("UMyClass") + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0),//类名,+1去掉U、A、F前缀,+11去掉Deprecated_前缀
PrivateStaticClass, //输出引用,所以值会被改变
StaticRegisterNativesUMyClass, //注册类Native函数的指针
sizeof(UMyClass), //类大小
UMyClass::StaticClassFlags, //类标记,值为CLASS_Intrinsic,表示在C++代码里定义的类
UMyClass::StaticClassCastFlags(), //虽然是调用,但只是简单返回值CASTCLASS_None
UMyClass::StaticConfigName(), //配置文件名,用于从config里读取值
(UClass::ClassConstructorType)InternalConstructor<UMyClass>,//构造函数指针,包了一层
(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyClass>,//hotreload的时候使用来构造虚函数表,暂时不管
&UMyClass::AddReferencedObjects, //GC使用的添加额外引用对象的静态函数指针,若没有定义,则会调用到UObject::AddReferencedObjects,默认函数体为空。
&UMyClass::Super::StaticClass, //获取基类UClass*的函数指针,这里Super是UObject
&UMyClass::WithinClass::StaticClass //获取对象外部类UClass*的函数指针,默认是UObject
);
}
return PrivateStaticClass;
}
里面基本上就是简单的传值给GetPrivateStaticClassBody。
- Package名字的传入是为了在构建UClass*之后,把UClass*对象的OuterPrivate设定为正确的UPackage*对象。在UE里,UObject必须属于某个UPackage。所以传入名字是为了后续查找或者创建出前置需要的UPackage对象。“/Script/”开头表示这是个代码模块。
StaticRegisterNativesUMyClass
这个函数的名字是用宏拼接的,分别在.generated.h和.gen.cpp里声明和定义。InternalConstructor<UMyClass>
这个模板函数是为了包一下C++的构造函数,因为你没法直接去获得C++构造函数的函数指针。在.generated.h里会根据情况生成这两个宏的调用