【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef
基本概念
- UE4 对 UObject 对象提供垃圾回收
- UE4 对原生对象不提供垃圾回收,需要手动进行清理
- 方式
- malloc / free
- new / delete
new与malloc的区别在于,new在分配内存完成之后会调用构造函数。
- 缺点
- 如果不及时清理,则会占用内存,或者导致内存泄漏
- 如果不小心提前清理,则会导致野指针
- 方式
- UE4 提供共享指针库来管理内存,它是C++11智能指针的自定义实现
- 分类
- TSharedPtr
- UniquePtr
- TWeakPtr
- TSharedRef
- 优点
- 防止内存泄漏 共享引用不存在时,智能指针(弱指针除外)会自动删除对象。
- 弱引用 弱指针会中断引用循环并阻止悬挂指针。
- 可选择的线程安全 虚幻智能指针库包括线程安全代码,可跨线程管理引用计数。如无需线程安全,可用其换取更好性能。
- 运行时安全 共享引用从不为空,可固定随时取消引用。
- 授予意图 可轻松区分对象所有者和观察者。
- 内存 智能指针在64位下仅为C++指针大小的两倍(加上共享的16字节引用控制器)。唯一指针除外,其与C++指针大小相同。
- 分类
共享指针 TSharedPtr
- TSharedPtr 不能指向 UObject。如果想要指向UObject,可以使用TWeakObjectPtr
- TSharedPtr 可以对FStructures 使用
创建/初始化/ 重置
MakeShareable()/MakeShared<T>()
函数Reset()
函数class SimpleObject {
public:
SimpleObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"SimpleObject Construct")); }
~SimpleObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"SimpleObject Destruct")); }
void ExeFun() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"Execute")); }
};
// 快速创建共享指针
TSharedPtr<SimpleObject> simObjectPtr(new SimpleObject());
// MakeShareable 创建共享指针
TSharedPtr<SimpleObject> simObjectPtr2 = MakeShareable(new SimpleObject());
// 创建线程安全
TSharedPtr<SimpleObject, ESPMode::ThreadSafe> simObjectPtr3 = MakeShareable(new SimpleObject());
// 查看引用计数 UE_LOG(LogTemp, Warning,
TEXT(__FUNCTION__"引用计数: simObjectPtr[%d], simObjectPtr2[%d], simObjectPtr3[%d] "),
simObjectPtr.GetSharedReferenceCount(), simObjectPtr2.GetSharedReferenceCount(), simObjectPtr3.GetSharedReferenceCount()); // 重置共享指针
simObjectPtr.Reset();
simObjectPtr2 = nullptr;
复制/转移
赋值
MoveTemp / MoveTempIfPossible
// 复制共享指针
TSharedPtr<SimpleObject> simObjectPtr_copy = simObjectPtr;
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"引用计数: simObjectPtr[%d], simObjectPtr_copy[%d],"),
simObjectPtr.GetSharedReferenceCount(), simObjectPtr_copy.GetSharedReferenceCount()); // 转移共享指针
TSharedPtr<SimpleObject> simObjectPtr_MoveTemp = MoveTemp(simObjectPtr_copy); // 另 MoveTempIfPossible()
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"引用计数: simObjectPtr[%d], simObjectPtr_copy[%d], simObjectPtr_MoveTemp[%d]"),
simObjectPtr.GetSharedReferenceCount(), simObjectPtr_copy.GetSharedReferenceCount(), simObjectPtr_MoveTemp.GetSharedReferenceCount()
条件判断 / 对比 / 解引用与访问
->
运算符Get()
函数IsValid()
函数==
!=
运算符if (simObjectPtr) // 条件判断
{
simObjectPtr->ExeFun(); // 解引用
}
if (simObjectPtr.Get() != nullptr) // 条件判断
{
simObjectPtr.Get()->ExeFun(); //解引用
}
if (simObjectPtr.IsValid()) // 条件判断
{
(*simObjectPtr).ExeFun(); // 解引用
}
if (simObjectPtr == simObjectPtr_copy) // 对比
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"simObjectPtr_copy == simObjectPtr"));
}
共享引用 TSharedRef
- 共享引用不可为空
- 不可用于 UObject对象
- 没有 IsValid() 函数
创建/初始化
MakeShareable()/MakeShared<T>()
函数// 创建共享引用
TSharedRef<SimpleObject> objRef(new SimpleObject());
TSharedRef<SimpleObject> objRef2 = MakeShareable(new SimpleObject());
TSharedRef<SimpleObject> objRef3 = MakeShared<SimpleObject>();
TSharedRef 与 TSharedPtr转换
隐式转化
ToSharedRef()
// TSharedRef -> TSharedPtr
TSharedPtr<SimpleObject> objPtr = objRef; // TSharedPtr -> TSharedRef
objRef3= objPtr.ToSharedRef();
比较
没有 IsValid() 函数
// 共享指针比较
if (objRef == objRef3)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"objRef == objRef3 , 引用计数:%d"), objPtr.GetSharedReferenceCount());
}
弱指针 TWeakPtr
- 与TSharedPtr相比,不参与引用计数
- 对象不存在共享指针时,TWeakPtr将自动失效
- 使用时需要判断有效性
创建/初始化/转换/重置
通过 TSharedPtr 创建
通过 TSharedRef 创建
运算符
=
赋值IsValid()
函数判断有效性Pin()
函数转成 TSharedPtr ,再解引用访问对象Reset()
或nullptr
重置// 强指针创建弱指针
TSharedPtr<SimpleObject> ObjPtr=MakeShared<SimpleObject>();
TWeakPtr<SimpleObject> ObjWeakPtr(ObjPtr);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"step1 引用计数:ObjPtr[%d]"), ObjPtr.GetSharedReferenceCount()); //强引用创建弱指针
TSharedRef<SimpleObject> objRef = MakeShareable(new SimpleObject());
TWeakPtr<SimpleObject> ObjWeakPtr2(objRef); TWeakPtr<SimpleObject> ObjWeakPtr_Copy = ObjWeakPtr;
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"step2 引用计数:ObjPtr[%d]"), ObjPtr.GetSharedReferenceCount()); // 判断有效性
if (ObjWeakPtr.IsValid())
{
TSharedPtr<SimpleObject> ObjPtr2 = ObjWeakPtr.Pin();
ObjPtr2->ExeFun();
} // 清空强指针
ObjPtr.Reset();
TSharedPtr<SimpleObject> ObjPtr2 = ObjWeakPtr.Pin();
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"step3 引用计数:ObjPtr[%d]"), ObjPtr.GetSharedReferenceCount()); // 判断有效性
if (!ObjPtr2)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"弱指针已空 "));
} // 重置
ObjWeakPtr.Reset();
ObjWeakPtr_Copy = nullptr;
唯一指针 TUniquePtr
- TUniquePtr 指向的对象只能被唯一指向,因而 Unique指针不能赋值给其它指针
- 不要为共享指针或共享引用引用的对象创建唯一指针
创建/初始化/判断/解引用/重置
MakeUnique()
IsValid()
->
运算符Get()
函数Release()
释放并返回指针Reset()
或nullptr
重置// 创建唯一指针
TUniquePtr<SimpleObject> ObjUniquePtr = MakeUnique<SimpleObject>();
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Validity: ObjUniquePtr[%d]"), ObjUniquePtr.IsValid()); // 判断有效性
if (ObjUniquePtr.IsValid())
{
ObjUniquePtr->ExeFun(); // 解引用
} // 释放指针,移交
TUniquePtr<SimpleObject> ObjUniquePtr2(ObjUniquePtr.Release());
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Validity: ObjUniquePtr[%d], ObjUniquePtr2[%d]"), ObjUniquePtr.IsValid(), ObjUniquePtr2.IsValid()); // 重置
ObjUniquePtr.Reset();
ObjUniquePtr2 = nullptr;
基类与派生类的智能转换
共享指针转换
派生类转基类 隐式转换
基类转派生类 StaticCastSharedPtr
非常量转常量 ConstCastSharedPtr
TSharedPtr<SimpleObject> simpleObj;
TSharedPtr<ComplexObject> complexObj = MakeShared<ComplexObject>(); // 派生类转基类
simpleObj = complexObj;
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"simpleObj is %s"), simpleObj.IsValid() ? TEXT("Valid") : TEXT("Not Valid")); // 基类转派生类
TSharedPtr<ComplexObject> complexObj2 = StaticCastSharedPtr<ComplexObject>(simpleObj);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"complexObj2 is %s"), complexObj2.IsValid() ? TEXT("Valid") : TEXT("Not Valid")); // 常量指针转非常量指针 const TSharedPtr<SimpleObject> simpleObj_const(new SimpleObject());
TSharedPtr<SimpleObject> simpleObj_mutable = ConstCastSharedPtr<SimpleObject>(simpleObj_const);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"simpleObj_mutable is %s"), simpleObj_mutable.IsValid() ? TEXT("Valid") : TEXT("Not Valid"));
共享引用转换
- 隐式转换
- StaticCastSharedRef
// 创建唯一指针
TUniquePtr ObjUniquePtr = MakeUnique();
UE_LOG(LogTemp, Warning, TEXT(FUNCTION" Validity: ObjUniquePtr[%d]"), ObjUniquePtr.IsValid());
// 判断有效性
if (ObjUniquePtr.IsValid())
{
ObjUniquePtr->ExeFun(); // 解引用
}
// 释放指针,移交
TUniquePtr<SimpleObject> ObjUniquePtr2(ObjUniquePtr.Release());
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Validity: ObjUniquePtr[%d], ObjUniquePtr2[%d]"), ObjUniquePtr.IsValid(), ObjUniquePtr2.IsValid());
// 重置
ObjUniquePtr.Reset();
ObjUniquePtr2 = nullptr;
- ConstStaticCastSharedRef
代码省略
助手类 TSharedFromThis
自定义类继承 TSharedFromThis 模板类
TSharedFromThis 会保存一个弱指针
AsShared()
将裸指针转智共享引用,可再隐式转为共享指针SharedThis(this)
会返回具备"this"类型的TSharedRef不要在构造函数中调用 AsShared 或 Shared,共享引用此时并未初始化,将导致崩溃或断言
// 基类
class BaseClass :public TSharedFromThis<BaseClass>
{
public:
BaseClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
virtual ~BaseClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
virtual void ExeFun() {
TSharedRef<BaseClass> ThisAsSharedRef = AsShared();
}
}; // 派生类
class ChildClass :public BaseClass
{
public:
ChildClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
virtual ~ChildClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
virtual void ExeFun() override{
//AsShared()返回 TSharedRef<BaseClass>, 因而编译不通过
//TSharedRef<ChildClass> AsSharedRef = AsShared(); TSharedRef<ChildClass> AsSharedRef = SharedThis(this);
}
};
TSharedPtr<BaseClass> BaseClassPtr = MakeShared<BaseClass>();
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 引用计数:BaseClassPtr[%d]"), BaseClassPtr.GetSharedReferenceCount()); BaseClass* tempPtr = BaseClassPtr.Get();
TSharedPtr<BaseClass> BaseClassPtr_Shared =tempPtr->AsShared();
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 引用计数:BaseClassPtr[%d], BaseClassPtr_Shared[%d]"),
BaseClassPtr.GetSharedReferenceCount(), BaseClassPtr_Shared.GetSharedReferenceCount()); // 使用下面语句运行,程序死机
// TSharedPtr<BaseClass> BaseClassPtr_New = MakeShareable(tempPtr);
注意
- 避免将数据作为 TSharedRef 或 TSharedPtr 参数传到函数,此操作将因取消引用和引用计数而产生开销。相反,建议将引用对象作为 const & 进行传递。
- 共享指针与虚幻对象(UObject 及其衍生类)不兼容。引擎具有 UObject 管理的单独内存管理系统(对象处理文档),两个系统未互相重叠。
参考
【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef的更多相关文章
- 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载
同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...
- 【UE4 C++ 基础知识】<4> 枚举 Enum、结构体 Struct
枚举 UENUM宏搭配BlueprintType可以将枚举暴露给蓝图,不使用的话,仅能在C++使用 //定义一个原生enum class enum class EMyType { Type1, Typ ...
- 【UE4 C++ 基础知识】<12> 多线程——FRunnable
概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...
- 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换
基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...
- 【UE4 C++ 基础知识】<10>资源的引用
2种引用方式 硬引用(Hard Reference) 即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载 硬引用过多会导致运行时很多暂时用不到的资源也被加载到内存中 大量资源会导致进程阻 ...
- 【UE4 C++ 基础知识】<5> 容器——TArray
概述 TArray 是UE4中最常用的容器类.其速度快.内存消耗小.安全性高. 其设计时未考虑扩展问题,因此建议在实际操作中勿使用 新建(new) 和 删除(delete) 创建或销毁 TArray ...
- 【UE4 C++ 基础知识】<8> Delegate 委托
概念 定义 UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系. 监听者通过将响应函数绑定到委托上,使得委托触发时立即收到 ...
- 【UE4 C++ 基础知识】<6> 容器——TMap
概述 TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中. 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取 映 ...
- 【UE4 C++ 基础知识】<13> 多线程——TaskGraph
概述 TaskGraph 系统是UE4一套抽象的异步任务处理系统 TaskGraph 可以看作一种"基于任务的并行编程"设计思想下的实现 通过TaskGraph ,可以创建任意多线 ...
随机推荐
- Docker - 解决 docker push 上传镜像报:denied: requested access to the resource is denied 的问题
问题背景 在 Linux 已登录自己的 Docker hub 账号 上传本地镜像但是报错了 docker push tomcat 解决方案 docker tag tomcat poloyy/tomca ...
- 【机器学习|数学基础】Mathematics for Machine Learning系列之线性代数(1):二阶与三阶行列式、全排列及其逆序数
@ 目录 前言 二阶与三阶行列式 二阶行列式 三阶行列式 全排列及其逆序数 全排列 逆序数 结语 前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 自我介绍 ...
- C# List集合类常用操作:四、删除
Clear() 从 List<T> 中移除所有元素. List<Employees> employees = new List<Employees>(); empl ...
- WPF Prism8.0中注册Nlog日志服务
无论是Nlog还是Serilog, 它们都提供了如何快速在各类应用程序当中的快速使用方法. 尽管,你现在无论是在WPF或者ASP.NET Core当中, 都可以使用ServiceCollection来 ...
- CUDA 矩阵乘法终极优化指南
作者:马骏 | 旷视 MegEngine 架构师 前言 单精度矩阵乘法(SGEMM)几乎是每一位学习 CUDA 的同学绕不开的案例,这个经典的计算密集型案例可以很好地展示 GPU 编程中常用的优化技巧 ...
- (3)java Spring Cloud+Spring boot+mybatis企业快速开发架构之SpringCloud-Spring Cloud和Dubbo的区别及各自的优缺点
我们先从 Nginx 说起,了解为什么需要微服务.最初的服务化解决方案是给相同服务提供一个统一的域名,然后服务调用者向这个域发送 HTTP 请求,由 Nginx 负责请求的分发和跳转. 这种架构存 ...
- Dockerfile自动制作Docker镜像(二)—— 其它常用命令
Dockerfile自动制作Docker镜像(二)-- 其它常用命令 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云服务器 c. 上一篇:Dockerf ...
- 解决dede编辑器不能保存word文档样式问题
ckeditor在dede里不能保存样式,试过多种解决办法都还是没有解决.最终将编辑器换成FCK得到解决. 第一步:下载FCK编辑器 下载地址: 链接: http://pan.baidu.com/s/ ...
- LateX出坑
1 公式是用$ 包围着的 $ 2 \begin{equation} 里面的公式自动编号 \end{equation} 要达成这样的效果,暂时想到如下方法: 1 \begin{equation} ...
- javascript 实现php str_pad
* 查看php.net官方手册 string str_pad ( string $input , int $pad_length [, string $pad_string = " &quo ...