UObject 的 FObjectInitializer 构造函数的调用

注意:只有 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_BODYGENERATED_UCLASS_BODY来定义了两种 __DefaultConstructor 的实现,一种是调用 FObjectInitializer 的构造函数,另一种是调用类的默认构造函数。

所以,默认情况下 FObjectInitializer 的构造函数和 UObject 的默认构造函数在调用时只会走一个。

那么它是如何被调用到的呢?

NewObject 中调用的 StaticConstructObject_Internal 中有以下代码:

1
2
3
4
5
6
7
8
9
bool bRecycledSubobject = false;	
Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject);
check(Result != NULL);
// Don't call the constructor on recycled subobjects, they haven't been destroyed.
if (!bRecycledSubobject)
{
STAT(FScopeCycleCounterUObject ConstructorScope(InClass, GET_STATID(STAT_ConstructObject)));
(*InClass->ClassConstructor)(FObjectInitializer(Result, InTemplate, bCopyTransientsFromClassDefaults, true, InInstanceGraph) );
}

通过传入进来的 UClass 对象获取其中的 ClassConstructor 函数指针,并构造出一个 FObjectInitializer 作为参数传递。

在之前的笔记 (StaticClass 是如何定义的) 中,写到了,通过 GetPrivateStaticClass 获取到当前 UObject 类的 UClass 实例会实例化出一个当前类的 InternalConstructor 函数(注意这个模板参数 T 是 UObject 的类):

1
2
3
4
5
6
7
8
// class.h

// Helper template to call the default constructor for a class
template<class T>
void InternalConstructor(const FObjectInitializer& X )
{
T::__DefaultConstructor(X);
}

就将其转发到了上面讲到的 __DefaultConstructor 函数上,然后它里面又转发到了所传入 FObjectInitializer 对象上的 const FObjectInitializer& 构造函数上(或者默认构造函数上)。

创建对象并调用 const FObjectInitializer& 构造函数的调用流程为:

  1. 通过调用 StaticAllocateObject 根据传入的 UClass 创建出对象
  2. 通过传入的 UClass 对象内的 ClassConstructor 函数指针调用所创建 UObject 类的 InternalConstructor 函数
  3. UMyObject::InternalConstructor会转发到UMyObject::__DefaultConstructor
  4. UMyObject::__DefaultConstructor会从接收到的 FObjectInitializer 对象上获取到通过 StaticAllocateObject 创建的对象,然后通过 placement-new 的方式,在这块内存上调用 UMyObject 类的 const FObjectInitializer& 构造函数。

通过以上的流程就实现了 NewObject 时会自动调用到它的 FObjectInitializer 构造函数。

注意:在 CDO 的构造上有点区别,CDO 的构造是通过 UClass::GetDefaultObject 中实现上述流程的。