注意:只有 GENERATED_UCLASS_BODY 才可以实现
FObjectInitializer
的构造函数。
在继承自 UObject 的类中,都可以自己写一个接收 const FObjectInitializer&
参数的构造函数,在创建对象时会被调用:
1 | UMyObject::UMyObject(const FObjectInitializer& Initializer){} |
在类中的 GENERATED_UCLASS_BODY
中默认声明这样一个构造函数。并且,在 UHT 生成的代码中通过宏还定义了一个函数:
1 | DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyObject) |
这个宏经过展开之后就是这样的:
1 | static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyObject(X); } |
为当前的对象类型 UMyObject
定义了一个 __DefaultConstructor
函数,作用是在当前 FObjectInitializer
传入的参数的 Object 上调用 FObjectInitializer
的构造函数。
注意 :如果是GENERATED_BODY
则不会声明这个构造函数,使用的就是另一个宏:
1 | DEFINE_DEFAULT_CONSTRUCTOR_CALL(UMyObject) |
展开之后是这样的:
1 | static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyObject; } |
是通过 GANERATED_BODY
和GENERATED_UCLASS_BODY
来定义了两种 __DefaultConstructor
的实现,一种是调用 FObjectInitializer
的构造函数,另一种是调用类的默认构造函数。
所以,默认情况下 FObjectInitializer 的构造函数和 UObject 的默认构造函数在调用时只会走一个。
那么它是如何被调用到的呢?
在 NewObject
中调用的 StaticConstructObject_Internal
中有以下代码:
1 | bool bRecycledSubobject = false; |
通过传入进来的 UClass 对象获取其中的 ClassConstructor
函数指针,并构造出一个 FObjectInitializer
作为参数传递。
在之前的笔记 (StaticClass 是如何定义的) 中,写到了,通过 GetPrivateStaticClass
获取到当前 UObject 类的 UClass 实例会实例化出一个当前类的 InternalConstructor
函数(注意这个模板参数 T 是 UObject 的类):
1 | // class.h |
就将其转发到了上面讲到的 __DefaultConstructor
函数上,然后它里面又转发到了所传入 FObjectInitializer
对象上的 const FObjectInitializer&
构造函数上(或者默认构造函数上)。
创建对象并调用 const FObjectInitializer&
构造函数的调用流程为:
- 通过调用
StaticAllocateObject
根据传入的 UClass 创建出对象 - 通过传入的 UClass 对象内的 ClassConstructor 函数指针调用所创建 UObject 类的
InternalConstructor
函数 UMyObject::InternalConstructor
会转发到UMyObject::__DefaultConstructor
UMyObject::__DefaultConstructor
会从接收到的FObjectInitializer
对象上获取到通过StaticAllocateObject
创建的对象,然后通过placement-new
的方式,在这块内存上调用UMyObject
类的const FObjectInitializer&
构造函数。
通过以上的流程就实现了 NewObject
时会自动调用到它的 FObjectInitializer
构造函数。
注意:在 CDO 的构造上有点区别,CDO 的构造是通过
UClass::GetDefaultObject
中实现上述流程的。