原文地址:UE4 <指针> UObject智能指针
正文
Guoyoko/智能指针示例项目gitee.com/guoyoko03/ue4-guoyoko_pro
说在前面:在做 GC 相关测试时,实例化的对象,不建议使用
Actor
类型(Spawn 到场景中),而是新建一个空UObject
来进行测试(NewObject
),原因见这个链接 UE4 垃圾回收系统_案例<0>
0. 测试环境
新建一个 Actor
子类 MyActor
,使用蓝图继承,然后放入到关卡中.
新建一个 UObject
子类 MyObject
,用智能指针指向该类型的实例化对象.
.h
声明成员变量,然后再声明 UObject
智能指针成员变量,
class SMARTPOINTERSDEMO_API AMyActor : public AActor
{
... // 略去无关代码
TWeakObjectPtr<UObject> TestWeakObject;
TStrongObjectPtr<UObject> TestStrongObject;
FSoftObjectPtr TestFSoftObject;
TSoftObjectPtr<UObject> TestTSoftObject;
TSoftClassPtr<UObject> TestTSoftClass;
TLazyObjectPtr<UObject> TestLazyObject;
}
.cpp
在 BeginPlay
时,动态实例化 UObject
对象,并定义计时器,每隔一秒输出智能指针的状态。
在 Tick
函数中记录游戏总时间,在游戏时间达到 DestroyTime
的的时,调用智能指针的 Reset
方法,观察智能指针发生的变化. 在 Tick
函数中调用强制 GC 方法
下面的代码以 TWeakObjectPtr
为例
void AMyActor::BeginPlay()
{
TestWeakObject = NewObject<UMyObject>();
UE_LOG(SPLog, Display, TEXT("NewObject %s address is %p"), *TestWeakObject->GetName(), TestWeakObject.Get());
auto WeakObjectPtrFunc = [this]() {
if (TestWeakObject.Get())
{
UE_LOG(SPLog, Warning, TEXT("TestWeakObject address is %p"), TestWeakObject.Get());
}
else if (TestWeakObject.IsStale())
{
UE_LOG(SPLog, Warning, TEXT("TestWeakObject address is %p"), TestWeakObject.Get());
}
else
{
UE_LOG(SPLog, Error, TEXT("TestWeakObject address is %p"), TestWeakObject.Get());
}
};
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(TimerHandle, WeakObjectPtrFunc, 1.0f, true);
}
void AMyActor::Tick(float DeltaTime)
{
TotalTime += DeltaTime;
if (TotalTime >= DestoryTime)
{
TestWeakObject.Reset();
}
// 强制 GC
GEngine->ForceGarbageCollection(true);
}
1. TWeakObjectPtr
当 弱智能指针 所指向的对象销毁后,弱智能指针 就指向了 nullptr
调用 弱智能指针 的 Reset
方法,IsStale()
则返回 false
,在没有重置或没有被重新分配,返回的是 true
.
2. TStrongObjectPtr
关键代码替换
TestStrongObject = TStrongObjectPtr(NewObject<UMyObject>());
UE_LOG(SPLog, Display, TEXT("NewObject %s address is %p"), *TestStrongObject->GetName(), TestStrongObject.Get());
auto StrongObjectPtrFunc = [this]() {
if (TestStrongObject) {
UE_LOG(SPLog, Warning, TEXT("TestStrongObject address is %p"), TestStrongObject.Get());
}
else {
UE_LOG(SPLog, Error, TEXT("TestStrongObject address is %p"), TestStrongObject.Get());
}
};
-
阻止指向的对象被 GC
-
调用 强智能指针 的
Reset
方法,原指向的对象被 GC
下面的智能指针,测试代码和上面大同小异,都是替换关键代码,观察智能指针的变化,代码部分就省略了.
3. FSoftObjectPtr
4. TSoftObjectPtr & TSoftClassPtr
5. TLazyObjectPtr
6. FSoftObjectPath & FSoftClassPath
7. UE C++ 与 蓝图变量类型的对应关系
-
图中引用对象的名字和类型一致
-
图中引用对象的名字和类型一致
8. 总结
硬对象指针:
所引用的 UObject
的持有者,除非指向该对象的所有硬指针都变为空,或者在对象上专门调用了 Destroy
,此时将解析为 nullptr
。阻止对象被 GC
软对象指针:
对象或资产的路径的字符串表示,需要时显式被加载。 它在内部存储了一个附加的弱指针,以在对象被查询和找到后(在非编辑器构建中)缓存它。不会阻止对象被 GC
弱对象指针:
被创建或设置为指向已经使用其 GUObjectArray
索引实例化的现有 UObject
。 指针不需要是 UPROPERTY
就可以知道它指向的对象是否已被垃圾回收。不会阻止对象被 GC
本文只是从使用的角度,展示了各个 UObject
智能指针,对源码的分析也有很多的博文,知其然知其所以然 .
- FSoftObjectPtr: 不推荐使用。
TSoftObjectPtr
的非模板化和非 BP 公开版本 - TSoftObjectPtr: 推荐使用。用于引用可能会或可能不会通过其路径加载的对象。即使没有加载,也可以指向其他关卡中的 Actor。当指向资产(如网格)以临时加载它们时,可以与异步加载函数一起使用
- TSoftClassPtr: 推荐使用。用于引用可能加载或未加载的类或蓝图类型。加载后,它将为您提供一个可以从中创建实例的类类型。可以与异步加载函数一起使用来加载它们
- TWeakObjectPtr: 推荐使用。用于引用已经实例化的对象。如果引用的对象被销毁或垃圾收集,将解析为
nullptr
- TStrongObjectPtr: 不推荐使用。引用的对象,强制不会被GC
- FSoftObjectPath: 不推荐使用。由上面的指针类型在内部使用,很慢,因为它不缓存结果
- FSoftClassPath: 不推荐使用。与
FSoftObjectPath
相同,但有一些与加载类相关的辅助函数
一句话:推荐使用带 T
的,也就是模板 弱对象智能指针和软对象智能指针
参考文章
- All about Soft and Weak pointers
- Pointer Types
- Bisher-d790 / UE4-SmartPointersDemo
- 【UE4 C++ 基础知识】<10>资源的引用
- UE4 垃圾回收系统_案例<0>
- (UE4 4.20)UE4 FSoftObjectPath,FSoftClassPath,FSoftObjectPtr,TSoftObjectPtr,TSoftClassPtr,TSubclassOf
- quabqi:UE4的智能指针 TSharedPtr
- quabqi:UE4的智能指针 UObject相关
- Unreal 多种指针实现原理
- Unreal Smart Pointer Library
- 云鸽:虚幻4:智能指针基础
- rayhunter:UE4智能指针及与STL的对比