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. 【转】sql server获取数据库名,表明,表结构

    1.查询数据库中的所有数据库名: SELECT Name FROM Master..SysDatabases ORDER BY Name 2.查询某个数据库中所有的表名: SELECT Name FR ...

  2. python基础知识---迭代器、生成器、装饰器

    一.迭代器 二.生成器 http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html def func(): #定义生成器,和普通函数的区别是 ...

  3. PHP在linux上执行外部命令

    PHP在linux上执行外部命令 一.PHP中调用外部命令介绍二.关于安全问题三.关于超时问题四.关于PHP运行linux环境中命令出现的问题 一.PHP中调用外部命令介绍在PHP中调用外部命令,可以 ...

  4. Buffer Cache

    Buffer Cache Buffer Cache是SGA区中专门用于存放从数据文件中读取的的数据块拷贝的区域.Oracle进程如果发现需要访问的数据块已经在buffer cache中,就直接读写内存 ...

  5. 一个Tomcat多端口多项目部署

    为什么突然写这个呢,前两天面试了,被面试官给问住了,回来一查.....怎么这么简单,当时我就....你懂得. 我用的是Tomcat7,用Eclipse新建了两个简单的web项目,贴出web1的截图,w ...

  6. 情感分析的现代方法(包含word2vec Doc2Vec)

    英文原文地址:https://districtdatalabs.silvrback.com/modern-methods-for-sentiment-analysis 转载文章地址:http://da ...

  7. 9.8 js进阶总结3

    DOM文档对象模型 DOM(document object model)文档对象模型,它定义了操作文档对象的接口. DOM 把一份html文档表示为一棵家谱树,使用parent(父),child(子) ...

  8. PHP中使用cURL实现Get和Post请求的方法

    1.cURL介绍  cURL 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP.FTP.TELNET等.最爽的是,PHP也支持 cURL 库.本文将介绍 cURL 的一些高级特 ...

  9. python-基础介绍

    一.Linux基础 - 计算机以及日后我们开发的程序防止的服务器的简单操作 二.Python开发 http://www.cnblogs.com/wupeiqi/articles/5433893.htm ...

  10. sqlserver存取过程游标

    ALTER proc [dbo].[common_proc_temp2] as begin declare @id varchar(50); declare @cbcontractid varchar ...