UObject是一套很复杂的体系,之前读ue3代码时曾分析过其类型系统实现,主要是与UClass间的关系

现在转到ue4,发现那一块其实差不多,于是再重点备忘一下UObject本身的创建和初始化过程

1、首先,用NewObject<>来创建一个新对象:

template< class T >
T* NewObject(UObject* Outer, UClass* Class, FName Name = NAME_None, EObjectFlags Flags = RF_NoFlags, UObject* Template = nullptr, bool bCopyTransientsFromClassDefaults = false, FObjectInstancingGraph* InInstanceGraph = nullptr)
{
if (Name == NAME_None)
{
FObjectInitializer::AssertIfInConstructor(Outer, TEXT("NewObject with empty name can't be used to create default subobjects (inside of UObject derived class constructor) as it produces inconsistent object names. Use ObjectInitializer.CreateDefaultSuobject<> instead."));
} #if DO_CHECK
// Class was specified explicitly, so needs to be validated
CheckIsClassChildOf_Internal(T::StaticClass(), Class);
#endif return static_cast<T*>(StaticConstructObject_Internal(Class, Outer, Name, Flags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InInstanceGraph));
}

除了一些条件检测,直接调了以前老版的对应物StaticConstructObject_Internal。

这里的几个参数,有意思的是第5个参数Template,通过它可以复制对象。

2、StaticConstructObject_Internal,这里面主要做两件事:

一是用StaticAllocateObject来分配空间,

二是在此空间上初始化对象。

3、StaticAllocateObject:

首先是检查要创建新对象,还是替换一个原有对象,因为一路传进来的有个Name参数。如果找到一个已有的同名对象,那么会强制那个对象析构,然后重用它的空间,否则的话,就会直接分配空间了:

UObjectBase* FUObjectAllocator::AllocateUObject(int32 Size, int32 Alignment, bool bAllowPermanent)

除了这件主要的工作,还有不少其它的事,比如管理对象之间的连接、在异步线程中创建对象时的通知等等,暂时不深究了。

4、有了前者分配的空间,那么就可以在此处构造对象了:

     Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject);
(*InClass->ClassConstructor)( FObjectInitializer(Result, InTemplate, bCopyTransientsFromClassDefaults, true, InInstanceGraph) );

这里ClassConstructor是每个UClass都有的一个函数指针类成员变量,实际上所有的UClass里该指针都指向一个全局模板函数:

template<class T>
void InternalConstructor( const FObjectInitializer& X )
{
T::__DefaultConstructor(X);
}

而每个类里的__DefaultConstructor也是用宏统一生成的,内容也是简单的转发给new:

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

这个new的形式很不平凡,既有传给operator new的【(EInternal*)X.GetObj()】,又有传给该类实际构造函数的【FObjectInitializer& X】

后者正是InClass->ClassConstructor需要的实参,而前者也是通过宏(DECLARE_CLASS)定义在每个UObject里的一个operator new:

inline void* operator new( const size_t InSize, EInternal* InMem ) \
{ \
return (void*)InMem; \
}

总结一下上面绕来绕去的东西就是:

StaticAllocateObject分配了内存空间Result,然后以此为参数(当然还有其它的参数)构造了一个FObjectInitializer X,其中X.GetObj返回的就是这个内存地址Result;

接着用【 new(Result) TClass(X) 】来构造对象,指明地址在Result上,并将X做为参数传给其构造函数。

5、关于FObjectInitializer

接上步,在构造函数返回后,其临时参数【FObjectInitializer& X】是要自动析构的,因而ue4利用此步骤,在这里面做了很多事,大部份的事都是与该体系内部状态管理有关的,暂时难以透彻理解。

但有一个的操作与应用相关,即属性初始化,这是通过InitProperties来完成的:

void FObjectInitializer::InitProperties(UObject* Obj, UClass* DefaultsClass, UObject* DefaultData, bool bCopyTransientsFromClassDefaults)
{
  ……
UClass* Class = Obj->GetClass();
  ……if (!bNeedInitialize && bCanUsePostConstructLink)
{
// This is just a fast path for the below in the common case that we are not doing a duplicate or initializing a CDO and this is all native.
// We only do it if the DefaultData object is NOT a CDO of the object that's being initialized. CDO data is already initialized in the
// object's constructor.
if (DefaultData)
{
if (Class->GetDefaultObject(false) != DefaultData)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_InitProperties_FromTemplate);
for (UProperty* P = Class->PropertyLink; P; P = P->PropertyLinkNext)
{
P->CopyCompleteValue_InContainer(Obj, DefaultData);
}
}
else
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_InitProperties_ConfigEtcOnly);
// Copy all properties that require additional initialization (e.g. CPF_Config).
for (UProperty* P = Class->PostConstructLink; P; P = P->PostConstructLinkNext)
{
P->CopyCompleteValue_InContainer(Obj, DefaultData);
}
}
}
  ……

在这里通过遍历UClass上记录的属性元数据,可以对当前实例的每个属性进行赋值。

有趣的是DefaultData这个参数,也就是最早的Template参数,一路辗转到此。当然如果Template为空的话,这里的DefaultData就是该类的CDO了。

代码里明显体现出对此两种情况的不同处理策略:

如果是指定了要复制的对象Template->DefaultData,那么要遍历类上的所有的属性,因为对于一个实际的复制目标,你不知道它哪些属性已经改变了(不是默认值 ),因此必须全盘复制

而如果只是从CDO复制的话,那么只需要处理该类里明确指定过可能有初始化状态的字段,如打上CPF_Config标记的字段,它们在启动时会去ini文件里提取相应的配置值。

ue4 NewObject/StaticConstructObject_Internal/StaticAllocateObject/FObjectInitializer:对象创建和初始化的更多相关文章

  1. [你必须知道的.NET]第十九回:对象创建始末(下)

    本文将介绍以下内容: 对象的创建过程 内存分配分析 内存布局研究 接上回[第十八回:对象创建始末(上)],继续对对象创建话题的讨论>>> 2.2 托管堆的内存分配机制 引用类型的实例 ...

  2. Java中对象创建过程

    本文介绍的对象创建过程仅限于普通Java对象,不包括数组和Class对象. 1.类加载检查 虚拟机遇到一条new指令时,首先去检查该指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用 ...

  3. Progress.js – 为页面上的任意对象创建进度条效果

    Progress.js 是一个 JavaScript 和 CSS3 的库,它帮助开发人员为网页上的每个对象创建和管理进度条效果.你可以设计自己的模板,进度条或者干脆定制. 您可以使用 Progress ...

  4. 《JavaScript模式》第5章 对象创建模式

    @by Ruth92(转载请注明出处) 第5章:对象创建模式 JavaScript 是一种简洁明了的语言,并没有其他语言中经常使用的一些特殊语法特征,如 命名空间.模块.包.私有属性 以及 静态成员 ...

  5. C#与数据库访问技术总结(六)之Command对象创建SQl语句代码示例

    Command对象创建SQl语句代码示例 说明:前面介绍了 Command 对象的方法和一些属性,回顾一下 Command对象主要用来执行SQL语句.利用Command对象,可以查询数据和修改数据. ...

  6. javascript一种新的对象创建方式-Object.create()

    1.Object.create() 是什么? Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不 ...

  7. 【JavaScript回顾】对象创建的几种模式

    组合使用构造函数模式和原型模式 创建自定义类型的常见方式,就是组合使用构造函数模式与原型模式.构造函数模式用于定义实 例属性,而原型模式用于定义方法和共享的属性.结果,每个实例都会有自己的一份实例属性 ...

  8. Cocos2d-js中使用纹理对象创建Sprite对象

    本节我们会通过一个实例介绍纹理对象创建Sprite对象使用,这个实例如图5-2所示,其中地面上的草是放在背景(如下图所示)中的,场景中的两棵树是从后图所示的“树”纹理图片中截取出来的,图5-5所示是树 ...

  9. 实例:使用纹理对象创建Sprite对象

    精灵类是Sprite,它的类图如下图所示: Sprite类直接继承了Node类,具有Node基本特征.此外,我们还可以看到Sprite类的派生类有:PhysicsSprite和Skin.Physics ...

随机推荐

  1. 基于springmvc和restClient的rest服务的测试

    在开发中,不熟悉驱动驱动测试开发的coder往往喜欢将服务应用启动以后测试,对于GET请求可以直接在浏览器中输入URL就可以,参数非中文可以直接追加到URL后.但是,对于其他请求方式的测试必须借助相应 ...

  2. 《Benign and maligenant breast tumors classification based on region growing and CNN segmentation》翻译阅读与理解

    注明:本人英语水平有限,翻译不当之处,请以英文原版为准,不喜勿喷,另,本文翻译只限于学术交流,不涉及任何版权问题,若有不当侵权或其他任何除学术交流之外的问题,请留言本人,本人立刻删除,谢谢!! 另:欢 ...

  3. [转载]jquery中attr和prop的区别

    在高版本的jquery引入prop方法后,什么时候该用prop?什么时候用attr?它们两个之间有什么区别?这些问题就出现了. 关于它们两个的区别,网上的答案很多.这里谈谈我的心得,我的心得很简单: ...

  4. poi批量导入excel文件

    package com.practice.util; import java.io.File; import java.io.FileInputStream; import java.io.FileN ...

  5. ---awk 调shell 命令的方法

    cat /etc/passwd | awk -F: '/root/{ system ("echo "$1) }'

  6. scala-尾递归

    ------------------------- by chenkh ----------------------------- 随笔记录什么是尾递归,为什么需要尾递归,尾递归show by exa ...

  7. 短视频APP+不同类型社交应用发展分析+化妆品电商

    短视频APP——昙花一现还是发展趋势? 在这个互联网与科技并行且飞速发展的时代,各种app不断涌入市场,其中短视频app便是一个典型,美拍,就成功入围2014年十大最火app.而短视频app也势必要成 ...

  8. 并发编程中.net与java的一些对比

    Java在并发编程中进行使用java.util.concurrent.atomic来处理一些轻量级变量 如AtomicInteger AtomicBoolean等 .Net中则使用Interlocke ...

  9. LinQ的组合+分页

    前台代码: 名称:<asp:TextBox ID="Textname" runat="server"></asp:TextBox> 油耗 ...

  10. 用EasyWebSvr搭建Axure本地访问地址-转载加完善

    1.下载之后解压到任意一个位置,可以是桌面(反正很小不占空间),如图2:: 图2 解压之后文件目录 2.将生成的原型放在EasyWebSvr根目录下的demo之中,如图3所示: 图3  原型文件放置目 ...