【UE4 C++】UObject 创建、销毁、内存管理

UObject 的创建

NewObject 模板类

  • 本例使用 UE 4.26,只剩下 NewObject 用来创建 UObject,提供两个带不同可选参数构造函数的模板类

  • Outer 表示这个对象的外部对象,通常可传 this 指针进去

  • Name 为对象名,如果没有自定义,默认生成,自带 GetName() 方法获取

    template<class T>
    T* NewObject(UObject* Outer)
    {
    	T* Object = ::NewObject<T>(Outer);
    	Object->SetInternalFlags(EInternalObjectFlags::Async);
    	return Object;
    }
    
    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)
    {
    	T* Object = ::NewObject<T>(Outer, Class, Name, Flags, Template, bCopyTransientsFromClassDefaults, InInstanceGraph);
    	Object->SetInternalFlags(EInternalObjectFlags::Async);
    	return Object;
    }
    

实践

  • 创建一个 UObject 类

    UCLASS()
    class TIPS_API UItemObject : public UObject
    {
    	GENERATED_BODY()
    	FString m_Name;
    public:
    	UItemObject() { 
    		m_Name = GetName();
    		UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s"), *m_Name);
    	}
    
    	~UItemObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s"), *m_Name); }
    };
    
  • 创建 UObject 实例

    UItemObject* Obj = NewObject<UItemObject>();
    UItemObject* Obj2 = NewObject<UItemObject>(this, TEXT("Obj2"));
    

UObject 的销毁

自动销毁

  • UObject及其派生 具有被 UE4 垃圾回收机制管理,因而当指向对象的指针为 nullptr 后,将会被 UE4 自动回收掉

    Obj = NewObject<UItemObject>(this, TEXT("Obj"));
    Obj = nullptr;
    

主动销毁

  • UObject::ConditionalBeginDestroy()

    • 异步执行且对象在当前帧内持续有效
    • 等待下次GC
    Obj->ConditionalBeginDestroy();
    Obj = nullptr;
    
  • MarkPendingKill()

    • 标记为PendingKill,等待回收。指向此实例的指针将设置为NULL,并在下一次GC时删除。
    • IsPendingKill 判断是否处于 PendingKill 状态
    • ClearPendingKill 清除 PendingKill 状态
    Obj->MarkPendingKill();
    Obj = nullptr;
    
  • Engine\Config \BaseEngine.ini 更改下面参数,设置销毁时间间隔

    gc.TimeBetweenPurgingPendingKillObjects=60
    

强制垃圾回收

  • UWorld::ForceGarbageCollection 弃用

  • GEngine->ForceGarbageCollection

    GEngine->ForceGarbageCollection(true);
    

原生对象内存管理

new/delete

  • 需要手动清理,易造成内存泄漏

  • delete 一般将指针置为 nullptr ,防止指向的地址不固定

    class SimpleObject {
    public:
    	SimpleObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
    	~SimpleObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
    };
    
    SimpleObject* Obj = new SimpleObject();
    
    delete Obj;
    Obj = nullptr;
    

使用智能指针

  • 对于非 UObject 的对象,可以使用智能指针进行管理
  • 当引用计数为0时,对象自动销毁

UObject 对象内存管理

UPROPERTY() 保持引用

  • 支持容器 TArray、TMap 的 <UObject*>类型。TArray、TMap使用UPROPERTY() ,也可以使元素对象常驻内存

    UPROPERTY()
    	class UItemObject* m_ItemObject2;
    
    UPROPERTY()
    	TArray<class UItemObject*> m_ObjList1;
    

AddToRoot 和 RemoveFromRoot 标记不被GC/移除标记

  • 构造时 和 AddToRoot 一起

  • 析构时和 RemveFromRoot 一起

    //创建对象
    m_ItemObject1 = NewObject<UItemObject>(this,TEXT("m_ItemObject1"));
    m_ItemObject1->AddToRoot();
    
    // 释放对象
    m_ItemObject1->RemoveFromRoot();
    m_ItemObject1 = nullptr;
    

FStreamableManager 资源卸载

  • FStreamableHandle::ReleaseHandle()

    TSharedPtr<FStreamableHandle> Handle  = UAssetManager::GetStreamableManager().RequestSyncLoad(AssetPath);
    UObject* Obj = Handle->GetLoadedAsset();
    Handle->ReleaseHandle();
    

AActor 销毁

  • Destroy() 方法

UActorComponent

  • DestroyComponent() 方法

扩展:FGCObjectScopeGuard

<think>嗯,用户这次聚焦在UE5C++基类的创建原则和规范上,看来是希望从工程实践层面掌握具体准则。结合之前的对话,用户应该已经理解UE5基类的类型体系,现在需要落地的技术细节。用户可能正在开发UE5项目,遇到了基类设计的具体问题,比如如何选择继承层次、避免内存泄漏等。从ta两次询问基类相关问题的连贯性看,可能是中级C++开发者,但对UE5特有的框架规范还不熟悉。需要重点强调几个实践要点:首先是UCLASS宏的必须性,这是UE反射系统的核心;其次是构造函数和InitializeComponent()的调用顺序规范,这个在引擎底层有严格要求;最后是垃圾回收的智能指针使用,直接关系到项目稳定性。注意到用户提供的引用中,[^3]特别强调了虚析构函数,这在UE里其实被UObject自动处理了,但要提醒用户自定义非UObject基类仍需注意。另外[^2]提到的BeginPlay/Tick重写规范也要展开说明,因为几乎所有Actor都会用到。技术规范要分层次说明:最底层是UObject强制规则(如宏标记),中间层是生命周期函数重写准则(BeginPlay优于构造函数),顶层是派生类设计建议(避免多重继承)。性能相关的Tick规范也值得单独强调,这是大型项目的优化重点。最后考虑设计的问题要帮助用户延伸到具体场景:Pawn/Controller的关系设计、RPC函数规范都是联机游戏的关键点,可能正是用户下一步需要的。</think> 在UE5中使用C++创建基类需遵循以下核心原则和规范,结合引擎架构和C++最佳实践: --- ### 一、核心创建原则 1. **继承自UObject体系** UE5中所有需支持反射、垃圾回收或蓝图集成的基类必须直接或间接继承`UObject`: ```cpp // 基类声明规范 UCLASS() class MYPROJECT_API UMyBaseClass : public UObject { GENERATED_BODY() }; ``` - **`UCLASS()`宏**:启用反射系统(支持序列化、蓝图调用)[^1] - **`GENERATED_BODY()`**:必须位于类内第一行,生成反射代码 2. **区分继承类型** - **场景对象** → 继承`AActor`(含Transform,可放置到关卡) ```cpp UCLASS() class AMyActor : public AActor { ... }; ``` - **功能模块** → 继承`UActorComponent`(可附加到Actor) - **数据容器** → 使用`USTRUCT()`(非UObject,轻量级数据)[^2] 3. **虚析构函数强制规范** 多态基类必须声明虚析构函数,防止内存泄漏: ```cpp virtual ~UMyBaseClass() = default; // 或实现清理逻辑[^3] ``` --- ### 二、关键编码规范 #### 1. 生命周期函数 | 函数 | 调用时机 | 规范建议 | |-------------|-----------------------|-----------------------------------| | **构造函数** | 对象创建时 | 仅初始化简单数据,避免复杂逻辑[^1] | | `BeginPlay()` | Actor加入场景后 | 执行初始化逻辑,替代构造函数 | | `Tick()` | 每帧更新(默认启用) | 需手动设置`PrimaryActorTick.bCanEverTick = true` | | `EndPlay()` | 对象销毁或离开场景 | 资源释放和状态清理 | ```cpp // 正确重写生命周期函数[^2] void AMyActor::BeginPlay() { Super::BeginPlay(); // 必须调用父类实现 // 自定义初始化 } void AMyActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); // 每帧逻辑(性能敏感) } ``` #### 2. 内存管理规范 - **垃圾回收(GC)** 通过`UPROPERTY()`标记引用关系,避免UObject被意外回收: ```cpp UPROPERTY() AActor* ReferencedActor; // GC自动跟踪引用 ``` - **智能指针** 非UObject对象使用`TUniquePtr`/`TSharedPtr`: ```cpp TSharedPtr<FMyData> PersistentData; ``` #### 3. 反射系统集成 | 宏 | 作用 | |----------------|-------------------------------| | `UPROPERTY()` | 暴露变量到蓝图和编辑器 | | `UFUNCTION()` | 暴露函数到蓝图调用 | | `UENUM()` | 暴露枚举类型 | ```cpp // 暴露属性和方法示例 UCLASS() class UMyComponent : public UActorComponent { GENERATED_BODY() UPROPERTY(EditAnywhere, Category="Settings") float MoveSpeed = 100.0f; // 编辑器可调参数 UFUNCTION(BlueprintCallable) void CustomAction(); // 蓝图可调用函数 }; ``` --- ### 三、性能优化要点 1. **Tick函数优化** - 非必要对象禁用Tick:`PrimaryActorTick.bCanEverTick = false` - 降低Tick频率:`SetActorTickInterval(0.2f)` 2. **组件化设计** 功能拆分为`UActorComponent`,按需启用/禁用 3. **内存控制** 大尺寸数据使用`TArray`+`InlineAllocator`减少堆分配 --- ### 四、设计实践建议 1. **遵循单一职责原则** 基类职责明确(如`AMovementController`仅处理移动逻辑) 2. **避免多重继承** UE反射系统不支持多继承 3. **使用接口类** 跨类型功能通过`UInterface`实现: ```cpp UINTERFACE() class UInteractable : public UInterface { ... }; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值