UE4对象系统_UObject&UClass

UObject

UObject为对象系统的基类。类层次为:

UObjectBase
     UObjectBaseUtility
          UObject

UObjectBase和UObjectBaseUtility为辅助类,所有对象需要从UObject继承。

  • UObjectBase类
class COREUOBJECT_API UObjectBase
{
    friend class UObjectBaseUtility;
    friend COREUOBJECT_API class UClass* Z_Construct_UClass_UObject();
    friend class FUObjectArray; // for access to InternalIndex without revealing it to anyone else
    friend class FUObjectAllocator; // for access to destructor without revealing it to anyone else
    friend COREUOBJECT_API void UObjectForceRegistration(UObjectBase* Object);
    friend COREUOBJECT_API void InitializePrivateStaticClass(
        class UClass* TClass_Super_StaticClass,
        class UClass* TClass_PrivateStaticClass,
        class UClass* TClass_WithinClass_StaticClass,
        const TCHAR* PackageName,
        const TCHAR* Name
        );
protected:
    UObjectBase() :
         NamePrivate(NoInit)  // screwy, but the name was already set and we don't want to set it again
    {
    }
    /**
     * Constructor used for bootstrapping
     * @param   InFlags         RF_Flags to assign
     */
    UObjectBase( EObjectFlags InFlags );
public:
    /**
     * Constructor used by StaticAllocateObject
     * @param   InClass             non NULL, this gives the class of the new object, if known at this time
     * @param   InFlags             RF_Flags to assign
     * @param   InInternalFlags EInternalObjectFlags to assign
     * @param   InOuter             outer for this object
     * @param   InName              name of the new object
     */
    UObjectBase( UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName );

    /**
     * Final destructor, removes the object from the object array, and indirectly, from any annotations
     **/
    virtual ~UObjectBase();

    /**
     * Emit GC tokens for UObjectBase, this might be UObject::StaticClass or Default__Class
     **/
    static void EmitBaseReferences(UClass *RootClass);

protected:
    /**
     * Just change the FName and Outer and rehash into name hash tables. For use by higher level rename functions.
     *
     * @param NewName   new name for this object
     * @param NewOuter  new outer for this object, if NULL, outer will be unchanged
     */
    void LowLevelRename(FName NewName,UObject *NewOuter = NULL);

    /** Force any base classes to be registered first */
    virtual void RegisterDependencies() {}

    /** Enqueue the registration for this object. */
    void Register(const TCHAR* PackageName,const TCHAR* Name);
    
    /**
     * Convert a boot-strap registered class into a real one, add to uobject array, etc
     *
     * @param UClassStaticClass Now that it is known, fill in UClass::StaticClass() as the class
     */
    virtual void DeferredRegister(UClass *UClassStaticClass,const TCHAR* PackageName,const TCHAR* Name);

private:
    /**
     * Add a newly created object to the name hash tables and the object array
     *
     * @param Name name to assign to this uobject
     * @param InSetInternalFlags Internal object flags to be set on the object once it's been added to the array
     */
    void AddObject(FName Name, EInternalObjectFlags InSetInternalFlags);
public:
    /**
     * Checks to see if the object appears to be valid
     * @return true if this appears to be a valid object
     */
    bool IsValidLowLevel() const;

    /**
     * Faster version of IsValidLowLevel.
     * Checks to see if the object appears to be valid by checking pointers and their alignment.
     * Name and InternalIndex checks are less accurate than IsValidLowLevel.
     * @param bRecursive true if the Class pointer should be checked with IsValidLowLevelFast
     * @return true if this appears to be a valid object
     */
    bool IsValidLowLevelFast(bool bRecursive = true) const;

    /** 
     * Returns the unique ID of the object...these are reused so it is only unique while the object is alive.
     * Useful as a tag.
    **/
    FORCEINLINE uint32 GetUniqueID() const
    {
        return (uint32)InternalIndex;
    }
    FORCEINLINE UClass* GetClass() const  // 获取该对象所属的类
    {
        return ClassPrivate;
    }
    FORCEINLINE UObject* GetOuter() const // 获取对象的Outer
    {
        return OuterPrivate;
    }
    FORCEINLINE FName GetFName() const
    {
        return NamePrivate;
    }

protected:
    /**
     * Set the object flags directly
     *
     **/
    FORCEINLINE void SetFlagsTo( EObjectFlags NewFlags )
    {
        checkfSlow((NewFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as 0x%x but is trying to set flags to RF_AllFlags"), *GetFName().ToString(), (int)ObjectFlags);
        ObjectFlags = NewFlags;
    }
public:
    /**
     * Retrieve the object flags directly
     *
     * @return Flags for this object
     **/
    FORCEINLINE EObjectFlags GetFlags() const
    {
        checkfSlow((ObjectFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as RF_AllFlags"), *GetFName().ToString());
        return ObjectFlags;
    }

    /**
     *  Atomically adds the specified flags.
     *  Do not use unless you know what you are doing.
     *  Designed to be used only by parallel GC and UObject loading thread.
     */
    FORCENOINLINE void AtomicallySetFlags( EObjectFlags FlagsToAdd )
    {
        int32 OldFlags = 0;
        int32 NewFlags = 0;
        do 
        {
            OldFlags = ObjectFlags;
            NewFlags = OldFlags | FlagsToAdd;
        }
        while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags );
    }

    /**
     *  Atomically clears the specified flags.
     *  Do not use unless you know what you are doing.
     *  Designed to be used only by parallel GC and UObject loading thread.
     */
    FORCENOINLINE void AtomicallyClearFlags( EObjectFlags FlagsToClear )
    {
        int32 OldFlags = 0;
        int32 NewFlags = 0;
        do 
        {
            OldFlags = ObjectFlags;
            NewFlags = OldFlags & ~FlagsToClear;
        }
        while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags );
    }

private:

    /** Flags used to track and report various object states. This needs to be 8 byte aligned on 32-bit
        platforms to reduce memory waste 对象标记*/
    EObjectFlags                    ObjectFlags;

    /** Index into GObjectArray...very private.  全局对象数组中的索引*/
    int32                               InternalIndex;

    /** Class the object belongs to. */
    UClass*                         ClassPrivate;

    /** Name of this object */
    FName                           NamePrivate;

    /** Object this object resides in. */
    UObject*                        OuterPrivate;

    // This is used by the reinstancer to re-class and re-archetype the current instances of a class before recompiling
    friend class FBlueprintCompileReinstancer;
    void SetClass(UClass* NewClass);

#if HACK_HEADER_GENERATOR
    // Required by UHT makefiles for internal data serialization.
    friend struct FObjectBaseArchiveProxy;
#endif // HACK_HEADER_GENERATOR
};

该类主要作用是存放对象的名字、标记信息(ObjectFlags)、类型信息(ClassPrivate)、Outer对象和在对象管理数组中的索引。EInternalObjectFlags标记存放在全局对象管理数组的元素中(这是为了提高Cache命中率)。
对象标记(ObjectFlags)很重要,它被引擎用于表示对象加载、保存、编辑、垃圾回收和对象作用标识时使用,我们需要了解这边标记的意义。

/** 
 * Flags describing an object instance
 */
enum EObjectFlags
{
    // Do not add new flags unless they truly belong here. There are alternatives.
    // if you change any the bit of any of the RF_Load flags, then you will need legacy serialization
    RF_NoFlags                      = 0x00000000,   ///< No flags, used to avoid a cast

    // This first group of flags mostly has to do with what kind of object it is. Other than transient, these are the persistent object flags.
    // The garbage collector also tends to look at these.
    RF_Public                   =0x00000001,    ///< Object is visible outside its package.对象可以被其它包中对象引用
    RF_Standalone               =0x00000002,    ///< Keep object around for editing even if unreferenced. 在编辑器中,该对象即便没有被引用也不会被GC掉
    RF_MarkAsNative                 =0x00000004,    ///< Object (UField) will be marked as native on construction (DO NOT USE THIS FLAG in HasAnyFlags() etc)
    RF_Transactional            =0x00000008,    ///< Object is transactional.
    RF_ClassDefaultObject       =0x00000010,    ///< This object is its class's default object 每个类都有一个DefaultObject
    RF_ArchetypeObject          =0x00000020,    ///< This object is a template for another object - treat like a class default object  该对象作为模板,其它对象可克隆它
    RF_Transient                =0x00000040,    ///< Don't save object.  临时对象,不会被序列化

    // This group of flags is primarily concerned with garbage collection.
    RF_MarkAsRootSet                    =0x00000080,    ///< Object will be marked as root set on construction and not be garbage collected, even if unreferenced (DO NOT USE THIS FLAG in HasAnyFlags() etc)
    RF_TagGarbageTemp           =0x00000100,    ///< This is a temp user flag for various utilities that need to use the garbage collector. The garbage collector itself does not interpret it.

    // The group of flags tracks the stages of the lifetime of a uobject
    RF_NeedInitialization       =0x00000200,    ///< This object has not completed its initialization process. Cleared when ~FObjectInitializer completes
    RF_NeedLoad                 =0x00000400,    ///< During load, indicates object needs loading.
    RF_KeepForCooker            =0x00000800,    ///< Keep this object during garbage collection because it's still being used by the cooker
    RF_NeedPostLoad             =0x00001000,    ///< Object needs to be postloaded.
    RF_NeedPostLoadSubobjects   =0x00002000,    ///< During load, indicates that the object still needs to instance subobjects and fixup serialized component references
    RF_NewerVersionExists       =0x00004000,    ///< Object has been consigned to oblivion due to its owner package being reloaded, and a newer version currently exists
    RF_BeginDestroyed           =0x00008000,    ///< BeginDestroy has been called on the object.
    RF_FinishDestroyed          =0x00010000,    ///< FinishDestroy has been called on the object.

    // Misc. Flags
    RF_BeingRegenerated         =0x00020000,    ///< Flagged on UObjects that are used to create UClasses (e.g. Blueprints) while they are regenerating their UClass on load (See FLinkerLoad::CreateExport())
    RF_DefaultSubObject         =0x00040000,    ///< Flagged on subobjects that are defaults
    RF_WasLoaded                =0x00080000,    ///< Flagged on UObjects that were loaded
    RF_TextExportTransient      =0x00100000,    ///< Do not export object to text form (e.g. copy/paste). Generally used for sub-objects that can be regenerated from data in their parent object.
    RF_LoadCompleted            =0x00200000,    ///< Object has been completely serialized by linkerload at least once. DO NOT USE THIS FLAG, It should be replaced with RF_WasLoaded.
    RF_InheritableComponentTemplate = 0x00400000, ///< Archetype of the object can be in its super class
    RF_DuplicateTransient = 0x00800000, ///< Object should not be included in any type of duplication (copy/paste, binary duplication, etc.)
    RF_StrongRefOnFrame         = 0x01000000,   ///< References to this object from persistent function frame are handled as strong ones.
    RF_NonPIEDuplicateTransient     = 0x02000000,  ///< Object should not be included for duplication unless it's being duplicated for a PIE session
    RF_Dynamic = 0x04000000, // Field Only. Dynamic field - doesn't get constructed during static initialization, can be constructed multiple times
};

    // Special all and none masks
#define RF_AllFlags             (EObjectFlags)0x07ffffff    ///< All flags, used mainly for error checking

    // Predefined groups of the above
#define RF_Load                     ((EObjectFlags)(RF_Public | RF_Standalone | RF_Transactional | RF_ClassDefaultObject | RF_ArchetypeObject | RF_DefaultSubObject | RF_TextExportTransient | RF_InheritableComponentTemplate | RF_DuplicateTransient | RF_NonPIEDuplicateTransient)) // Flags to load from Unrealfiles.
#define RF_PropagateToSubObjects    ((EObjectFlags)(RF_Public | RF_ArchetypeObject | RF_Transactional | RF_Transient))      // Sub-objects will inherit these flags from their SuperObject.
/** Objects flags for internal use (GC, low level UObject code) */
enum class EInternalObjectFlags : int32
{
    None = 0,
    // All the other bits are reserved, DO NOT ADD NEW FLAGS HERE!
    ReachableInCluster = 1 << 23, /// External reference to object in cluster exists
    ClusterRoot = 1 << 24, ///< Root of a cluster
    Native = 1 << 25, ///< Native (UClass only).
    Async = 1 << 26, ///< Object exists only on a different thread than the game thread.
    AsyncLoading = 1 << 27, ///< Object is being asynchronously loaded.
    Unreachable = 1 << 28, ///< Object is not reachable on the object graph.
    PendingKill = 1 << 29, ///< Objects that are pending destruction (invalid for gameplay but valid objects)
    RootSet = 1 << 30, ///< Object will not be garbage collected, even if unreferenced.
    NoStrongReference = 1 << 31, ///< The object is not referenced by any strong reference. The flag is used by GC.

    GarbageCollectionKeepFlags = Native | Async | AsyncLoading,
    // Make sure this is up to date!
    AllFlags = ReachableInCluster | ClusterRoot | Native | Async | AsyncLoading | Unreachable | PendingKill | RootSet | NoStrongReference
};
ENUM_CLASS_FLAGS(EInternalObjectFlags);
  • UObjectBaseUtility类
    提供一些辅助函数: Flag设置和查询、Mark设置与查询、Class查询、名字查询、Linker信息(Linker为uasset加载器)。下面摘抄部分代码。
class COREUOBJECT_API UObjectBaseUtility : public UObjectBase
{
public:
    /***********************/
    /******** Flags ********/
    /***********************/

    /***********************/
    /******** Marks *******  UObjectMarks.cpp */
    /***********************/

    /***********************/
    /******** Names ********/
    /***********************/

    /**
     * Returns the fully qualified pathname for this object as well as the name of the class, in the format:
     * 'ClassName Outermost[.Outer].Name'.
     *
     * @param   StopOuter   if specified, indicates that the output string should be relative to this object.  if StopOuter
     *                      does not exist in this object's Outer chain, the result would be the same as passing NULL.
     *
     * @note    safe to call on NULL object pointers!
     */
    FString GetFullName( const UObject* StopOuter=NULL ) const;

    /**
     * Returns the fully qualified pathname for this object, in the format:
     * 'Outermost[.Outer].Name'
     *
     * @param   StopOuter   if specified, indicates that the output string should be relative to this object.  if StopOuter
     *                      does not exist in this object's Outer chain, the result would be the same as passing NULL.
     *
     * @note    safe to call on NULL object pointers!
     */
    FString GetPathName( const UObject* StopOuter=NULL ) const;

public:
    /**
     * Walks up the chain of packages until it reaches the top level, which it ignores.
     *
     * @param   bStartWithOuter     whether to include this object's name in the returned string
     * @return  string containing the path name for this object, minus the outermost-package's name
     */
    FString GetFullGroupName( bool bStartWithOuter ) const;

    /**
     * Returns the name of this object (with no path information)
     * 
     * @return Name of the object.
     */
    FORCEINLINE FString GetName() const
    {
        return GetFName().ToString();
    }

    /***********************/
    /******** Outer ********/
    /***********************/

    /** 
     * Walks up the list of outers until it finds the highest one.
     *
     * @return outermost non NULL Outer.   所有对象必须在某个包中(Package),UPackage对象除外。
     */
    UPackage* GetOutermost() const;

    /** 
     * Finds the outermost package and marks it dirty. 
     * The editor suppresses this behavior during load as it is against policy to dirty packages simply by loading them.
     *
     * @return false if the request to mark the package dirty was suppressed by the editor and true otherwise.
     */
    bool MarkPackageDirty() const;

    /***********************/
    /******** Class ********/
    /***********************/

    /***********************/
    /******** Linker ****  UObjectLinker.cpp */
    /***********************/

    /**
     * Returns the linker for this object.
     *
     * @return  a pointer to the linker for this object, or NULL if this object has no linker
     */
    class FLinkerLoad* GetLinker() const;
    /**
     * Returns this object's LinkerIndex.
     *
     * @return  the index into my linker's ExportMap for the FObjectExport
     *          corresponding to this object.
     */
    int32 GetLinkerIndex() const;
};
  • UObject类
    提供如下功能:
    • 创建子对象(SubObject)
    • 构函数之后的调用
    • 对象Destroy相关事件处理
    • 对象编辑相关事件处理
    • 序列化
    • 执行脚本
    • 从config文件读取或保存成员变量配置.
      该类接口较多,请参考源文件Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h

UClass

C++语言不像C#,Java那样提供完整的反射功能,我们需要定义一个数据结构(UClass)来描述C++中的类信息,这个数据结构也称为类的元数据。当然在UE4中UClass实例不仅仅用于描述C++(Native)类,也用来描述Blueprint生成的类。
源码路径Engine\Source\Runtime\CoreUObject\Public\UObject\Class.h
类继承层次:

UObject
   UField
      UEnum
      UProperty
         UBoolProperty
         UEnumProperty
         UNumericProperty
         UObjectProperty
         ... 
      UStruct
         UClass
         UFunction
         UScriptStruct   

这块类的命名很有意思, 为什么叫UField呢参考Field In Java,类的成员变量、函数参数称为Field可以理解,但是UEunm, UStruct,UClass,UFunction也称为Field就有点不直观了,我估计是Enum,Struct,Class也是可以在类体中进行定义的原因。UFunction从UStruct继承也是有道理的,因为Function里有局部变量和参数这些Field。

  • UStruct类
/**
 * Base class for all UObject types that contain fields.
 */
class COREUOBJECT_API UStruct : public UField
{
    DECLARE_CASTED_CLASS_INTRINSIC(UStruct, UField, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UStruct)

    // Variables.
protected:
    friend COREUOBJECT_API UClass* Z_Construct_UClass_UStruct();
private:
    UStruct* SuperStruct;   // 父类, UE4对象系统只允许单继承关系
public:
    UField* Children;          // 拥有的成员变量和Function等其它Filed
    int32 PropertiesSize;    // 对象占用的内存大小

    int32 MinAlignment;      // 内存地址对齐要求
    
    TArray<uint8> Script;    // 脚本字节码,  UE4脚本是面向对象的脚本系统。 

    /** In memory only: Linked list of properties from most-derived to base **/
    UProperty* PropertyLink;   // 属性列表
    /** In memory only: Linked list of object reference properties from most-derived to base **/
    UProperty* RefLink;  // 对象引用的属性列表
    /** In memory only: Linked list of properties requiring destruction. Note this does not include things that will be destroyed byt he native destructor **/
    UProperty* DestructorLink;  // 需要析构的属性列表
    /** In memory only: Linked list of properties requiring post constructor initialization.**/
    UProperty* PostConstructLink;

    /** Array of object references embedded in script code. Mirrored for easy access by realtime garbage collection code */
    TArray<UObject*> ScriptObjectReferences;  // 脚本字节码中引用的对象列表

    /** Map of Class Name to Map of Old Property Name to New Property Name */
    static TMap<FName,TMap<FName,FName> > TaggedPropertyRedirects;
    static void InitTaggedPropertyRedirectsMap();

public:
    // Constructors.
    UStruct( EStaticConstructor, int32 InSize, EObjectFlags InFlags );
    explicit UStruct(UStruct* InSuperStruct, SIZE_T ParamsSize = 0, SIZE_T Alignment = 0);
    explicit UStruct(const FObjectInitializer& ObjectInitializer, UStruct* InSuperStruct, SIZE_T ParamsSize = 0, SIZE_T Alignment = 0 );

    // UObject interface.
    virtual void Serialize(FArchive& Ar) override;
    virtual void FinishDestroy() override;
    virtual void RegisterDependencies() override;
    static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
    virtual void GetPreloadDependencies(TArray<UObject*>& OutDeps) override;

    // UField interface.
    virtual void AddCppProperty(UProperty* Property) override;

    UProperty* FindPropertyByName(FName InName) const;

    /**
     * Creates new copies of components
     * 
     * @param   Data                        pointer to the address of the subobject referenced by this UProperty
     * @param   DefaultData                 pointer to the address of the default value of the subbject referenced by this UProperty
     * @param   DefaultStruct               the struct corresponding to the buffer pointed to by DefaultData
     * @param   Owner                       the object that contains the component currently located at Data
     * @param   InstanceGraph               contains the mappings of instanced objects and components to their templates
     */
    void InstanceSubobjectTemplates( void* Data, void const* DefaultData, UStruct* DefaultStruct, UObject* Owner, FObjectInstancingGraph* InstanceGraph );

    //
    virtual UStruct* GetInheritanceSuper() const {return GetSuperStruct();}

    //
    void StaticLink(bool bRelinkExistingProperties = false);

    //
    virtual void Link(FArchive& Ar, bool bRelinkExistingProperties);

    virtual void SerializeBin( FArchive& Ar, void* Data ) const;

    /**
     * Serializes the class properties that reside in Data if they differ from the corresponding values in DefaultData
     *
     * @param   Ar              the archive to use for serialization
     * @param   Data            pointer to the location of the beginning of the property data
     * @param   DefaultData     pointer to the location of the beginning of the data that should be compared against
     * @param   DefaultStruct   the struct corresponding to the block of memory located at DefaultData 
     */
    void SerializeBinEx( FArchive& Ar, void* Data, void const* DefaultData, UStruct* DefaultStruct ) const;

    virtual void SerializeTaggedProperties( FArchive& Ar, uint8* Data, UStruct* DefaultsStruct, uint8* Defaults, const UObject* BreakRecursionIfFullyLoad=NULL) const;

    /**
     * Initialize a struct over uninitialized memory. This may be done by calling the native constructor or individually initializing properties
     *
     * @param   Dest        Pointer to memory to initialize
     * @param   ArrayDim    Number of elements in the array
     * @param   Stride      Stride of the array, If this default (0), then we will pull the size from the struct
     */
    virtual void InitializeStruct(void* Dest, int32 ArrayDim = 1) const;
    /**
     * Destroy a struct in memory. This may be done by calling the native destructor and then the constructor or individually reinitializing properties
     *
     * @param   Dest        Pointer to memory to destory
     * @param   ArrayDim    Number of elements in the array
     * @param   Stride      Stride of the array. If this default (0), then we will pull the size from the struct
     */
    virtual void DestroyStruct(void* Dest, int32 ArrayDim = 1) const;

#if WITH_EDITOR
public:
    virtual UProperty* CustomFindProperty(const FName InName) const { return NULL; };
#endif // WITH_EDITOR
public:
    virtual EExprToken SerializeExpr(int32& iCode, FArchive& Ar);
    virtual void TagSubobjects(EObjectFlags NewFlags) override;

    /**
     * Returns the struct/ class prefix used for the C++ declaration of this struct/ class.
     *
     * @return Prefix character used for C++ declaration of this struct/ class.
     */
    virtual const TCHAR* GetPrefixCPP() const { return TEXT("F"); }

    FORCEINLINE int32 GetPropertiesSize() const
    {
        return PropertiesSize;
    }

    FORCEINLINE int32 GetMinAlignment() const
    {
        return MinAlignment;
    }

    FORCEINLINE int32 GetStructureSize() const
    {
        return Align(PropertiesSize,MinAlignment);
    }

    void SetPropertiesSize( int32 NewSize )
    {
        PropertiesSize = NewSize;
    }

    template<class T>
    bool IsChildOf() const
    {
        return IsChildOf(T::StaticClass());
    }

    bool IsChildOf( const UStruct* SomeBase ) const
    {
        for (const UStruct* Struct = this; Struct; Struct = Struct->GetSuperStruct())
        {
            if (Struct == SomeBase)
                return true;
        }

        return false;
    }

    UStruct* GetSuperStruct() const
    {
        return SuperStruct;
    }

    /**
     * Sets the super struct pointer and updates hash information as necessary.
     * Note that this is not sufficient to actually reparent a struct, it simply sets a pointer.
     */
    virtual void SetSuperStruct(UStruct* NewSuperStruct);

    /**
     * Serializes the SuperStruct pointer.
     */
    virtual void SerializeSuperStruct(FArchive& Ar);

    void LinkChild(UField* Child)
    {
        Child->Next = Children;
        Children = Child;
    }

    virtual FString PropertyNameToDisplayName(FName InName) const 
    { 
        return InName.ToString();
    }

#if WITH_EDITOR
    /** Try and find boolean metadata with the given key. If not found on this class, work up hierarchy looking for it. */
    bool GetBoolMetaDataHierarchical(const FName& Key) const;

    /** Try and find string metadata with the given key. If not found on this class, work up hierarchy looking for it. */
    bool GetStringMetaDataHierarchical(const FName& Key, FString* OutValue = nullptr) const;

    /**
    * Determines if the struct or any of its super structs has any metadata associated with the provided key
    *
    * @param Key The key to lookup in the metadata
    * @return pointer to the UStruct that has associated metadata, nullptr if Key is not associated with any UStruct in the hierarchy
    */
    const UStruct* HasMetaDataHierarchical(const FName& Key) const;
#endif

#if HACK_HEADER_GENERATOR
    // Required by UHT makefiles for internal data serialization.
    friend struct FStructArchiveProxy;
#endif // HACK_HEADER_GENERATOR

protected:

    /** Returns the property name from the guid */
    virtual FName FindPropertyNameFromGuid(const FGuid& PropertyGuid) const { return NAME_None; }

    /** Find property guid */
    virtual FGuid FindPropertyGuidFromName(const FName InName) const { return FGuid(); }

    /** Returns if we have access to property guids */
    virtual bool ArePropertyGuidsAvailable() const { return false; }

};
  • UClass类
    UClass实例描述了C++类和脚本类的反射信息,部分代码如下:
/**
 * An object class.
 */
class COREUOBJECT_API UClass : public UStruct
#if UCLASS_FAST_ISA_IMPL == UCLASS_ISA_INDEXTREE
    , private FFastIndexingClassTreeRegistrar
#elif UCLASS_FAST_ISA_IMPL == UCLASS_ISA_CLASSARRAY
    , private FClassBaseChain
#endif
{
    DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UClass, UStruct, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UClass, NO_API)
    DECLARE_WITHIN(UPackage)

public:

    typedef void        (*ClassConstructorType)             (const FObjectInitializer&);
#if WITH_HOT_RELOAD_CTORS
    typedef UObject*    (*ClassVTableHelperCtorCallerType)  (FVTableHelper& Helper);
#endif // WITH_HOT_RELOAD_CTORS
    typedef void        (*ClassAddReferencedObjectsType)    (UObject*, class FReferenceCollector&);
    typedef UClass* (*StaticClassFunctionType)();

    ClassConstructorType ClassConstructor;   // 所反射的类的构造函数
#if WITH_HOT_RELOAD_CTORS
    ClassVTableHelperCtorCallerType ClassVTableHelperCtorCaller;
#endif // WITH_HOT_RELOAD_CTORS
    /** Pointer to a static AddReferencedObjects method. */
    ClassAddReferencedObjectsType ClassAddReferencedObjects;  // 在GC时这个有用

    // Class pseudo-unique counter; used to accelerate unique instance name generation
    int32 ClassUnique;

    // Class flags; See EClassFlags for more information, 类的标记
    uint32 ClassFlags;  

    // Cast flags used to accelerate dynamic_cast<T*> on objects of this type for common T
    EClassCastFlags ClassCastFlags;

    // The required type for the outer of instances of this class. 该类型的实例的Outer必须是ClassWithin的类实例
    UClass* ClassWithin;

    // This is the blueprint that caused the generation of this class, or NULL if it is a native compiled-in class
    UObject* ClassGeneratedBy; // 该UClass实例是谁创建的,  注意: Blueprint是只是个资源(类似C++源文件), 编译出来的才是UClass实例。


    //
    FName ClassConfigName;

    // Used to check if the class was cooked or not.
    bool bCooked;

    // List of replication records, 网络replicated需要的信息
    TArray<FRepRecord> ClassReps;

    // List of network relevant fields (properties and functions) 需要网络replicated的域
    TArray<UField*> NetFields;   

    virtual bool IsNameStableForNetworking() const override { return true; }        // For now, assume all classes have stable net names

    /**
     * Calls AddReferencedObjects static method on the specified object.
     *
     * @param This Object to call ARO on.
     * @param Collector Reference collector.
     */
    FORCEINLINE void CallAddReferencedObjects(UObject* This, FReferenceCollector& Collector) const
    {
        // The object must of this class type.
        check(This->IsA(this)); 
        // This is should always be set to something, at the very least to UObject::ARO
        check(ClassAddReferencedObjects != NULL); 
        ClassAddReferencedObjects(This, Collector);
    }

    // The class default object; used for delta serialization and object initialization
    UObject* ClassDefaultObject;

    /**
    * Assemble reference token streams for all classes if they haven't had it assembled already. 每个类都会生成一个引用token流,这个给GC使用。
    */
    static void AssembleReferenceTokenStreams();

private:
        // ==================== 函数相关信息 ======================
    /** Map of all functions by name contained in this class */
    TMap<FName, UFunction*> FuncMap;

    /** A cache of all functions by name that exist in a parent context */
    mutable TMap<FName, UFunction*> ParentFuncMap;

    /** A cache of all functions by name that exist in an interface context */
    mutable TMap<FName, UFunction*> InterfaceFuncMap;

public:
    /**
     * The list of interfaces which this class implements, along with the pointer property that is located at the offset of the interface's vtable.
     * If the interface class isn't native, the property will be NULL.
     **/
    TArray<FImplementedInterface> Interfaces;

    /**
     * Prepends reference token stream with super class's stream.
     *
     * @param SuperClass Super class to prepend stream with.
     */
    void PrependStreamWithSuperClass(UClass& SuperClass);

    /** Reference token stream used by realtime garbage collector, finalized in AssembleReferenceTokenStream */
    FGCReferenceTokenStream ReferenceTokenStream;
    /** CS for the token stream. Token stream can assemble code can sometimes be called from two threads throuh a web of async loading calls. */
    FCriticalSection ReferenceTokenStreamCritical;

#if !(UE_BUILD_TEST || UE_BUILD_SHIPPING)
    /* TokenIndex map to look-up token stream index origin. */
    FGCDebugReferenceTokenMap DebugTokenMap;
#endif

    

    /** This class's native functions. */
    TArray<FNativeFunctionLookup> NativeFunctionLookupTable;

public:
    /**
     * Add a native function to the internal native function table
     * @param   InName                          name of the function
     * @param   InPointer                       pointer to the function
     **/
    void AddNativeFunction(const ANSICHAR* InName, Native InPointer);

    /**
     * Add a native function to the internal native function table, but with a unicode name. Used when generating code from blueprints, 
     * which can have unicode identifiers for functions and properties.
     * @param   InName                          name of the function
     * @param   InPointer                       pointer to the function
     **/
    void AddNativeFunction(const WIDECHAR* InName, Native InPointer);

    // Add a function to the function map
    void AddFunctionToFunctionMap(UFunction* NewFunction)
    {
        FuncMap.Add(NewFunction->GetFName(), NewFunction);
    }

    // This is used by the code generator, which instantiates UFunctions with a name that is later overridden. Overridden names
    // are needed to support generated versions of blueprint classes, properties of which do not have the same naming 
    // restrictions as native C++ properties.
    void AddFunctionToFunctionMapWithOverriddenName(UFunction* NewFunction, FName OverriddenName)
    {
        FuncMap.Add(OverriddenName, NewFunction);
    }

    // Remove a function from the function map
    void RemoveFunctionFromFunctionMap(UFunction* Function)
    {
        FuncMap.Remove(Function->GetFName());
    }

    void ClearFunctionMapsCaches()
    {
        ParentFuncMap.Empty();
        InterfaceFuncMap.Empty();
    }

    UFunction* FindFunctionByName(FName InName, EIncludeSuperFlag::Type IncludeSuper = EIncludeSuperFlag::IncludeSuper) const;

    // UField interface.
    virtual void Bind() override;
    virtual const TCHAR* GetPrefixCPP() const override;
    // End of UField interface.

    // UStruct interface.
    virtual void Link(FArchive& Ar, bool bRelinkExistingProperties) override;
    virtual void SetSuperStruct(UStruct* NewSuperStruct) override;
    virtual void SerializeSuperStruct(FArchive& Ar) override;
    // End of UStruct interface.
    
    /**
     * Translates the hardcoded script config names (engine, editor, input and 
     * game) to their global pendants and otherwise uses config(myini) name to
     * look for a game specific implementation and creates one based on the
     * default if it doesn't exist yet.
     *
     * @return  name of the class specific ini file
     */
    const FString GetConfigName() const;

    UClass* GetSuperClass() const
    {
        return (UClass*)GetSuperStruct();
    }

    /** Feedback context for default property import **/
    static class FFeedbackContext& GetDefaultPropertiesFeedbackContext();

    int32 GetDefaultsCount()
    {
        return ClassDefaultObject != NULL ? GetPropertiesSize() : 0;
    }

    /**
      * Get the default object from the class,  每个类都有一个CDO
      * @param  bCreateIfNeeded if true (default) then the CDO is created if it is NULL.
      * @return     the CDO for this class
    **/
    UObject* GetDefaultObject(bool bCreateIfNeeded = true)
    {
        if (ClassDefaultObject == NULL && bCreateIfNeeded)
        {
            CreateDefaultObject();
        }

        return ClassDefaultObject;
    }

    /**
    * Get the name of the CDO for the this class
    * @return The name of the CDO
    */
    FName GetDefaultObjectName();

    /** Searches for the default instanced object (often a component) by name **/
    UObject* GetDefaultSubobjectByName(FName ToFind);

    /** Adds a new default instance map item **/
    void AddDefaultSubobject(UObject* NewSubobject, UClass* BaseClass)
    {
        // this compoonent must be a derived class of the base class
        check(NewSubobject->IsA(BaseClass));
        // the outer of the component must be of my class or some superclass of me
        check(IsChildOf(NewSubobject->GetOuter()->GetClass()));
    }

    /**
     * Gets all default instanced objects (often components).
     *
     * @param OutDefaultSubobjects An array to be filled with default subobjects.
     */
    void GetDefaultObjectSubobjects(TArray<UObject*>& OutDefaultSubobjects);

    /** 
     * Purges out the properties of this class in preparation for it to be regenerated
     * @param bRecompilingOnLoad - true if we are recompiling on load
     */
    virtual void PurgeClass(bool bRecompilingOnLoad);

    virtual UObject* FindArchetype(UClass* ArchetypeClass, const FName ArchetypeName) const { return nullptr; }

    // This signature intentionally hides the method declared in UObject to make it private.
    // Call FindFunctionByName instead; This method will search for a function declared in UClass instead of the class it was called on
    UFunction* FindFunction(FName InName) const
    {
        return UObject::FindFunction(InName);
    }
};

StaticClass()

在Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectMacros.h定义了DECLARE_CLASS, IMPLEMENT_CLASS等关于class的宏。

/*-----------------------------------------------------------------------------
Class declaration macros.
-----------------------------------------------------------------------------*/

#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI  ) \
private: \
    TClass& operator=(TClass&&);   \
    TClass& operator=(const TClass&);   \
    TRequiredAPI static UClass* GetPrivateStaticClass(const TCHAR* Package); \
public: \
    /** Bitwise union of #EClassFlags pertaining to this class.*/ \
    enum {StaticClassFlags=TStaticFlags}; \
    /** Typedef for the base class ({{ typedef-type }}) */ \
    typedef TSuperClass Super;\
    /** Typedef for {{ typedef-type }}. */ \
    typedef TClass ThisClass;\
    /** Returns a UClass object representing this class at runtime */ \
    inline static UClass* StaticClass() \
    { \
        return GetPrivateStaticClass(TPackage); \
    } \
    /** Returns the StaticClassFlags for this class */ \
    inline static EClassCastFlags StaticClassCastFlags() \
    { \
        return TStaticCastFlags; \
    } \
    DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead") \
    inline void* operator new( const size_t InSize, UObject* InOuter=(UObject*)GetTransientPackage(), FName InName=NAME_None, EObjectFlags InSetFlags=RF_NoFlags ) \
    { \
        return StaticAllocateObject( StaticClass(), InOuter, InName, InSetFlags ); \
    } \
    /** For internal use only; use StaticConstructObject() to create new objects. */ \
    inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \
    { \
        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \
} \
    /** For internal use only; use StaticConstructObject() to create new objects. */ \
    inline void* operator new( const size_t InSize, EInternal* InMem ) \
    { \
        return (void*)InMem; \
    }
// Register a class at startup time.
#define IMPLEMENT_CLASS(TClass, TClassCrc) \
    static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \
       // 获取UClass实例
    UClass* TClass::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, \
                (TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \
                PrivateStaticClass, \
                StaticRegisterNatives##TClass, \
                sizeof(TClass), \
                TClass::StaticClassFlags, \
                TClass::StaticClassCastFlags(), \
                TClass::StaticConfigName(), \
                (UClass::ClassConstructorType)InternalConstructor<TClass>, \
                (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
                &TClass::AddReferencedObjects, \
                &TClass::Super::StaticClass, \
                &TClass::WithinClass::StaticClass \
            ); \
        } \
        return PrivateStaticClass; \
    }

// Used for intrinsics, this sets up the boiler plate, plus an initialization singleton, which can create properties and GC tokens
#define IMPLEMENT_INTRINSIC_CLASS(TClass, TRequiredAPI, TSuperClass, TSuperRequiredAPI, InitCode) \
    IMPLEMENT_CLASS(TClass, 0) \
    TRequiredAPI UClass* Z_Construct_UClass_##TClass(); \
    UClass* Z_Construct_UClass_##TClass() \
    { \
        static UClass* Class = NULL; \
        if (!Class) \
        { \
            extern TSuperRequiredAPI UClass* Z_Construct_UClass_##TSuperClass(); \
            UClass* SuperClass = Z_Construct_UClass_##TSuperClass(); \
            Class = TClass::StaticClass(); \
            UObjectForceRegistration(Class); \
            check(Class->GetSuperClass() == SuperClass); \
            InitCode \
            Class->StaticLink(); \
        } \
        check(Class->GetClass()); \
        return Class; \
    } \
    static FCompiledInDefer Z_CompiledInDefer_UClass_##TClass(Z_Construct_UClass_##TClass, &TClass::StaticClass, TEXT(#TClass), false);

其中静态成员函数StaticClass()返回一个UClass实例。主要工作在GetPrivateStaticClassBody()中。

void GetPrivateStaticClassBody(
    const TCHAR* PackageName,   // 包名
    const TCHAR* Name,   // Class名称
    UClass*& ReturnClass,
    void(*RegisterNativeFunc)(),
    uint32 InSize,
    uint32 InClassFlags,
    EClassCastFlags InClassCastFlags,
    const TCHAR* InConfigName,
    UClass::ClassConstructorType InClassConstructor,
    UClass::ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller,
    UClass::ClassAddReferencedObjectsType InClassAddReferencedObjects,
    UClass::StaticClassFunctionType InSuperClassFn,
    UClass::StaticClassFunctionType InWithinClassFn,
    bool bIsDynamic /*= false*/
    )
{
#if WITH_HOT_RELOAD
      // ...  Hot Reload情况
#endif

    if (!bIsDynamic)
    {
        ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), ALIGNOF(UClass), true);
        ReturnClass = ::new (ReturnClass)
            UClass
            (
            EC_StaticConstructor,
            Name,
            InSize,
            InClassFlags,
            InClassCastFlags,
            InConfigName,
            EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet),
            InClassConstructor,
#if WITH_HOT_RELOAD_CTORS
            InClassVTableHelperCtorCaller,
#endif // WITH_HOT_RELOAD_CTORS
            InClassAddReferencedObjects
            );
        check(ReturnClass);
    }
    else
    {
        ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UDynamicClass), ALIGNOF(UDynamicClass), GIsInitialLoad);
        ReturnClass = ::new (ReturnClass)
            UDynamicClass
            (
            EC_StaticConstructor,
            Name,
            InSize,
            InClassFlags,
            InClassCastFlags,
            InConfigName,
            EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_Dynamic | (GIsInitialLoad ? RF_MarkAsRootSet : RF_NoFlags)),
            InClassConstructor,
#if WITH_HOT_RELOAD_CTORS
            InClassVTableHelperCtorCaller,
#endif // WITH_HOT_RELOAD_CTORS
            InClassAddReferencedObjects
            );
        check(ReturnClass);
    }
    InitializePrivateStaticClass(
        InSuperClassFn(),
        ReturnClass,
        InWithinClassFn(),
        PackageName,
        Name
        );

    // Register the class's native functions(C++导出给bp用的函数).
    RegisterNativeFunc();
}

案例分析

本节分析一个自定义类,查看下与反射信息相关的代码。该例子是UE4的FPS Example。

Paste_Image.png

FirstPersonCharacter.h源码:

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Character.h"
#include "FirstPersonCharacter.generated.h"   // 由UnrealHeaderTool生成的头文件,定义一些与反射相关的宏,在FirstPerson\Intermediate\Build\Win64\UE4Editor\Inc\FirstPerson中

class UInputComponent;

UCLASS(config=Game) // 类的附加的meta-data定义
class AFirstPersonCharacter : public ACharacter
{
    GENERATED_BODY()        // 定义类反射宏

        // UPROPERTY宏的作用是告诉UHT, 这个成员变量需要加到反射里
    /** Pawn mesh: 1st person view (arms; seen only by self) */
    UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
    class USkeletalMeshComponent* Mesh1P;

    /** Gun mesh: 1st person view (seen only by self) */
    UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
    class USkeletalMeshComponent* FP_Gun;

    /** Location on gun mesh where projectiles should spawn. */
    UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
    class USceneComponent* FP_MuzzleLocation;

    /** Gun mesh: VR view (attached to the VR controller directly, no arm, just the actual gun) */
    UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
    class USkeletalMeshComponent* VR_Gun;

    /** Location on VR gun mesh where projectiles should spawn. */
    UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
    class USceneComponent* VR_MuzzleLocation;

    /** First person camera */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UCameraComponent* FirstPersonCameraComponent;

    /** Motion controller (right hand) */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    class UMotionControllerComponent* R_MotionController;

    /** Motion controller (left hand) */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    class UMotionControllerComponent* L_MotionController;

public:
    AFirstPersonCharacter();

protected:
    virtual void BeginPlay();

public:
    /** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
    float BaseTurnRate;

    /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
    float BaseLookUpRate;

    /** Gun muzzle's offset from the characters location */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
    FVector GunOffset;

    /** Projectile class to spawn */
    UPROPERTY(EditDefaultsOnly, Category=Projectile)
    TSubclassOf<class AFirstPersonProjectile> ProjectileClass;

    /** Sound to play each time we fire */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
    class USoundBase* FireSound;

    /** AnimMontage to play each time we fire */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
    class UAnimMontage* FireAnimation;

    /** Whether to use motion controller location for aiming. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
    uint32 bUsingMotionControllers : 1;

        UFUNCTION(BlueprintCallable)  // UFUNCTION告知UHT,需要反射该函数信息
    void BlueprintInvokeMe(bool bSuccess);

        //  .... 其它代码略去
};

UHT会扫描C++ .h文件,然后生成胶水层代码。一般是每个模块生成的代码放在一个文件中,本例放在
FirstPerson\Intermediate\Build\Win64\UE4Editor\Inc\FirstPerson\FirstPerson.generated.cpp中。

        // 针对每个函数创建一个UFunction对象。
    UFunction* Z_Construct_UFunction_AFirstPersonCharacter_BlueprintInvokeMe()
    {
        struct FirstPersonCharacter_eventBlueprintInvokeMe_Parms
        {
            bool bSuccess;
        };
        UObject* Outer=Z_Construct_UClass_AFirstPersonCharacter();
        static UFunction* ReturnFunction = NULL;
        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("BlueprintInvokeMe"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x04040401, 65535, sizeof(FirstPersonCharacter_eventBlueprintInvokeMe_Parms));
            CPP_BOOL_PROPERTY_BITMASK_STRUCT(bSuccess, FirstPersonCharacter_eventBlueprintInvokeMe_Parms, bool);
            UProperty* NewProp_bSuccess = new(EC_InternalUseOnlyConstructor, ReturnFunction, TEXT("bSuccess"), RF_Public|RF_Transient|RF_MarkAsNative) UBoolProperty(FObjectInitializer(), EC_CppProperty, CPP_BOOL_PROPERTY_OFFSET(bSuccess, FirstPersonCharacter_eventBlueprintInvokeMe_Parms), 0x0010000000000080, CPP_BOOL_PROPERTY_BITMASK(bSuccess, FirstPersonCharacter_eventBlueprintInvokeMe_Parms), sizeof(bool), true);
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();
#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
            MetaData->SetValue(ReturnFunction, TEXT("ToolTip"), TEXT("Set whether this actor's movement replicates to network clients."));
#endif
        }
        return ReturnFunction;
    }
    UClass* Z_Construct_UClass_AFirstPersonCharacter_NoRegister()
    {
        return AFirstPersonCharacter::StaticClass();
    }
        // 针对AFirstPersonCharacter类创建一个UClass对象。
    UClass* Z_Construct_UClass_AFirstPersonCharacter()
    {
        static UClass* OuterClass = NULL;
        if (!OuterClass)
        {
            Z_Construct_UClass_ACharacter();
            Z_Construct_UPackage__Script_FirstPerson();
            OuterClass = AFirstPersonCharacter::StaticClass();
            if (!(OuterClass->ClassFlags & CLASS_Constructed))
            {
                UObjectForceRegistration(OuterClass);
                OuterClass->ClassFlags |= 0x20800080;

                OuterClass->LinkChild(Z_Construct_UFunction_AFirstPersonCharacter_BlueprintInvokeMe());
// 针对每个成员变量,创建相应类型的UProperty实例。
PRAGMA_DISABLE_DEPRECATION_WARNINGS
                CPP_BOOL_PROPERTY_BITMASK_STRUCT(bUsingMotionControllers, AFirstPersonCharacter, uint8);
                UProperty* NewProp_bUsingMotionControllers = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("bUsingMotionControllers"), RF_Public|RF_Transient|RF_MarkAsNative) UBoolProperty(FObjectInitializer(), EC_CppProperty, CPP_BOOL_PROPERTY_OFFSET(bUsingMotionControllers, AFirstPersonCharacter), 0x0010000000000005, CPP_BOOL_PROPERTY_BITMASK(bUsingMotionControllers, AFirstPersonCharacter), sizeof(uint8), false);
                UProperty* NewProp_FireAnimation = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("FireAnimation"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(FireAnimation, AFirstPersonCharacter), 0x0010000000000005, Z_Construct_UClass_UAnimMontage_NoRegister());
                UProperty* NewProp_FireSound = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("FireSound"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(FireSound, AFirstPersonCharacter), 0x0010000000000005, Z_Construct_UClass_USoundBase_NoRegister());
                UProperty* NewProp_ProjectileClass = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("ProjectileClass"), RF_Public|RF_Transient|RF_MarkAsNative) UClassProperty(CPP_PROPERTY_BASE(ProjectileClass, AFirstPersonCharacter), 0x0014000000010001, Z_Construct_UClass_AFirstPersonProjectile_NoRegister(), UClass::StaticClass());
                UProperty* NewProp_GunOffset = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("GunOffset"), RF_Public|RF_Transient|RF_MarkAsNative) UStructProperty(CPP_PROPERTY_BASE(GunOffset, AFirstPersonCharacter), 0x0010000000000005, Z_Construct_UScriptStruct_FVector());
                UProperty* NewProp_BaseLookUpRate = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("BaseLookUpRate"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(BaseLookUpRate, AFirstPersonCharacter), 0x0010000000020015);
                UProperty* NewProp_BaseTurnRate = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("BaseTurnRate"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(BaseTurnRate, AFirstPersonCharacter), 0x0010000000020015);
                UProperty* NewProp_L_MotionController = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("L_MotionController"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(L_MotionController, AFirstPersonCharacter), 0x00400000000a001d, Z_Construct_UClass_UMotionControllerComponent_NoRegister());
                UProperty* NewProp_R_MotionController = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("R_MotionController"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(R_MotionController, AFirstPersonCharacter), 0x00400000000a001d, Z_Construct_UClass_UMotionControllerComponent_NoRegister());
                UProperty* NewProp_FirstPersonCameraComponent = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("FirstPersonCameraComponent"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(FirstPersonCameraComponent, AFirstPersonCharacter), 0x00400000000a001d, Z_Construct_UClass_UCameraComponent_NoRegister());
                UProperty* NewProp_VR_MuzzleLocation = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("VR_MuzzleLocation"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(VR_MuzzleLocation, AFirstPersonCharacter), 0x00400000000b0009, Z_Construct_UClass_USceneComponent_NoRegister());
                UProperty* NewProp_VR_Gun = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("VR_Gun"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(VR_Gun, AFirstPersonCharacter), 0x00400000000b0009, Z_Construct_UClass_USkeletalMeshComponent_NoRegister());
                UProperty* NewProp_FP_MuzzleLocation = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("FP_MuzzleLocation"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(FP_MuzzleLocation, AFirstPersonCharacter), 0x00400000000b0009, Z_Construct_UClass_USceneComponent_NoRegister());
                UProperty* NewProp_FP_Gun = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("FP_Gun"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(FP_Gun, AFirstPersonCharacter), 0x00400000000b0009, Z_Construct_UClass_USkeletalMeshComponent_NoRegister());
                UProperty* NewProp_Mesh1P = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("Mesh1P"), RF_Public|RF_Transient|RF_MarkAsNative) UObjectProperty(CPP_PROPERTY_BASE(Mesh1P, AFirstPersonCharacter), 0x00400000000b0009, Z_Construct_UClass_USkeletalMeshComponent_NoRegister());
PRAGMA_ENABLE_DEPRECATION_WARNINGS
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_AFirstPersonCharacter_BlueprintInvokeMe(), "BlueprintInvokeMe"); // 3698909481
                OuterClass->ClassConfigName = FName(TEXT("Game"));
                OuterClass->StaticLink();
#if WITH_METADATA
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
                MetaData->SetValue(OuterClass, TEXT("HideCategories"), TEXT("Navigation"));
                MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_bUsingMotionControllers, TEXT("Category"), TEXT("Gameplay"));
                MetaData->SetValue(NewProp_bUsingMotionControllers, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_bUsingMotionControllers, TEXT("ToolTip"), TEXT("Whether to use motion controller location for aiming."));
                MetaData->SetValue(NewProp_FireAnimation, TEXT("Category"), TEXT("Gameplay"));
                MetaData->SetValue(NewProp_FireAnimation, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_FireAnimation, TEXT("ToolTip"), TEXT("AnimMontage to play each time we fire"));
                MetaData->SetValue(NewProp_FireSound, TEXT("Category"), TEXT("Gameplay"));
                MetaData->SetValue(NewProp_FireSound, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_FireSound, TEXT("ToolTip"), TEXT("Sound to play each time we fire"));
                MetaData->SetValue(NewProp_ProjectileClass, TEXT("Category"), TEXT("Projectile"));
                MetaData->SetValue(NewProp_ProjectileClass, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_ProjectileClass, TEXT("ToolTip"), TEXT("Projectile class to spawn"));
                MetaData->SetValue(NewProp_GunOffset, TEXT("Category"), TEXT("Gameplay"));
                MetaData->SetValue(NewProp_GunOffset, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_GunOffset, TEXT("ToolTip"), TEXT("Gun muzzle's offset from the characters location"));
                MetaData->SetValue(NewProp_BaseLookUpRate, TEXT("Category"), TEXT("Camera"));
                MetaData->SetValue(NewProp_BaseLookUpRate, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_BaseLookUpRate, TEXT("ToolTip"), TEXT("Base look up/down rate, in deg/sec. Other scaling may affect final rate."));
                MetaData->SetValue(NewProp_BaseTurnRate, TEXT("Category"), TEXT("Camera"));
                MetaData->SetValue(NewProp_BaseTurnRate, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_BaseTurnRate, TEXT("ToolTip"), TEXT("Base turn rate, in deg/sec. Other scaling may affect final turn rate."));
                MetaData->SetValue(NewProp_L_MotionController, TEXT("AllowPrivateAccess"), TEXT("true"));
                MetaData->SetValue(NewProp_L_MotionController, TEXT("Category"), TEXT("FirstPersonCharacter"));
                MetaData->SetValue(NewProp_L_MotionController, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_L_MotionController, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_L_MotionController, TEXT("ToolTip"), TEXT("Motion controller (left hand)"));
                MetaData->SetValue(NewProp_R_MotionController, TEXT("AllowPrivateAccess"), TEXT("true"));
                MetaData->SetValue(NewProp_R_MotionController, TEXT("Category"), TEXT("FirstPersonCharacter"));
                MetaData->SetValue(NewProp_R_MotionController, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_R_MotionController, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_R_MotionController, TEXT("ToolTip"), TEXT("Motion controller (right hand)"));
                MetaData->SetValue(NewProp_FirstPersonCameraComponent, TEXT("AllowPrivateAccess"), TEXT("true"));
                MetaData->SetValue(NewProp_FirstPersonCameraComponent, TEXT("Category"), TEXT("Camera"));
                MetaData->SetValue(NewProp_FirstPersonCameraComponent, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_FirstPersonCameraComponent, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_FirstPersonCameraComponent, TEXT("ToolTip"), TEXT("First person camera"));
                MetaData->SetValue(NewProp_VR_MuzzleLocation, TEXT("Category"), TEXT("Mesh"));
                MetaData->SetValue(NewProp_VR_MuzzleLocation, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_VR_MuzzleLocation, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_VR_MuzzleLocation, TEXT("ToolTip"), TEXT("Location on VR gun mesh where projectiles should spawn."));
                MetaData->SetValue(NewProp_VR_Gun, TEXT("Category"), TEXT("Mesh"));
                MetaData->SetValue(NewProp_VR_Gun, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_VR_Gun, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_VR_Gun, TEXT("ToolTip"), TEXT("Gun mesh: VR view (attached to the VR controller directly, no arm, just the actual gun)"));
                MetaData->SetValue(NewProp_FP_MuzzleLocation, TEXT("Category"), TEXT("Mesh"));
                MetaData->SetValue(NewProp_FP_MuzzleLocation, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_FP_MuzzleLocation, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_FP_MuzzleLocation, TEXT("ToolTip"), TEXT("Location on gun mesh where projectiles should spawn."));
                MetaData->SetValue(NewProp_FP_Gun, TEXT("Category"), TEXT("Mesh"));
                MetaData->SetValue(NewProp_FP_Gun, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_FP_Gun, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_FP_Gun, TEXT("ToolTip"), TEXT("Gun mesh: 1st person view (seen only by self)"));
                MetaData->SetValue(NewProp_Mesh1P, TEXT("Category"), TEXT("Mesh"));
                MetaData->SetValue(NewProp_Mesh1P, TEXT("EditInline"), TEXT("true"));
                MetaData->SetValue(NewProp_Mesh1P, TEXT("ModuleRelativePath"), TEXT("FirstPersonCharacter.h"));
                MetaData->SetValue(NewProp_Mesh1P, TEXT("ToolTip"), TEXT("Pawn mesh: 1st person view (arms; seen only by self)"));
#endif
            }
        }
        check(OuterClass->GetClass());
        return OuterClass;
    }
        // 通过定义全局辅助FCompiledInDefer对象, 登记Class注册信息。
    static FCompiledInDefer Z_CompiledInDefer_UClass_AFirstPersonCharacter(Z_Construct_UClass_AFirstPersonCharacter, &AFirstPersonCharacter::StaticClass, TEXT("AFirstPersonCharacter"), false, nullptr, nullptr, nullptr);
    DEFINE_VTABLE_PTR_HELPER_CTOR(AFirstPersonCharacter);

备注:UProperty描述了成员变量的在类内存布局中的偏移量、占据的字节大小、类型、维度等信息,详情请参阅源码。

类注册调用堆栈(我用的是从UnrealLauncher下载的UE4, generated.cpp没法下断点):

UClass_Register.jpg

这里的机制是,在加载FirstPerson模块时(DLL),模块内的FCompiledInDefer全局变量会先执行初始化(构造函数中会进行登记),然后引擎对这些登记进行执行(例如执行Z_Construct_UClass_AFirstPersonCharacter()函数)。

总结

最后简单用下图描述UObject对象体系类与UClass的关系:

UE4_Relection_Diagram.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容