前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UE4智能指针

UE4智能指针

原创
作者头像
小伏羲
发布2018-12-18 19:27:53
7K0
发布2018-12-18 19:27:53
举报
文章被收录于专栏:UE4技术专场
  • 所有编译器和平台上有更加一致的实现
  • 可以和虚幻本身的类型更好的结合,比如容器等
  • 能更好的控制平台特性,包括线程的处理和优化(包含线程安全和非安全版本)
  • 添加了一些自己的改进,比如MakeShareable,赋值为NULL等
  • 有更多的控制权(内联函数,内存,虚函数等等)
  • 不需要的时候倾向不引入第三方库

Type

解释

内存占用

性能

TUniquePtr

专属所有权智能指针,只能转移,不能复制

默认和裸指针相同,有状态的删除其和采用函数指针实现的删除器会增加尺寸

基本裸指针相同

TSharedPtr

引用计数的非侵入式的权威智能指针

默认是裸指针两倍,但是多出了控制块的内存

有一定的内存消耗,大约是两倍

TSharedRef

引用计数的非侵入式的权威智能引用(只是不能为null)

同上

同上

TWeakPtr

引用计数的、非侵入式弱指针引用

两倍的裸指针大小,有一个指针指向控制块

比Tsharedptr略慢

代码语言:txt
复制
- 所有共享指针的内存消耗(8字节)(32位系统)
    - 源指针(32位)
    - 引用控制器(32位)
- 应用控制器(12字节)
    - 源指针
    - 共享引用计数
    - 弱引用计数使用的方法(以TSharedPtr为例)
代码语言:txt
复制
//声明一个类
Class MyClass
{
    public:
        int values;
};
代码语言:txt
复制
//分配以及初始化
TSharedPtr<MyClass> EmptySharedClass;	//这是一个空的共享指针
TSharedPtr<MyClass> FirstSharedObject(new MyClass());	//声明并初始化了一个共享指针
代码语言:txt
复制
//复制指针:
//当复制了一个共享指针,那引用数就会增加
TSharedPtr<MyClass> anotherObject = FirstSharedObject;
代码语言:txt
复制
//获取引用计数
FirstSharedObject.GetSharedReferenceCount();
代码语言:txt
复制
//解引用
FirstSharedObject->values;
FirstSharedObject.Get()->values;
(*FirstSharedObject).values;
代码语言:txt
复制
//释放当前指针
FirstSharedObject.Reset();
代码语言:txt
复制
//指针合法性
FirstSharedObject.IsValid()
或者:
FirstSharedObject.Get() != nullptr
  • 使用的场景(具体实例) - Unreal需要智能指针的地方(比如从TSharedFromThis继承的类,编辑器中比较常用) - FSkeletonEditor 中使用的 FSkeletonEditorModevoid FWorkflowCentricApplication::AddApplicationMode(FName ModeName, TSharedRef<FApplicationMode> Mode) { for (int32 Index = 0; Index < ModeExtenderList.Num(); ++Index) { Mode = ModeExtenderListIndex.Execute(ModeName, Mode); } ApplicationModeList.Add(ModeName, Mode); } - 没有自动内存管理的类,不想关心内存的释放(不是从Uobject继承的类型) - 扩展类型比如实现自己的Button// 内层依然使用的是SButton,这时候可能就需要智能指针管理 TSharedPtr<SButton> MyButton;
  • 智能指针的原理(以TSharedPtr为例介绍)
    • 总体概括图
Smart.jpg
Smart.jpg
代码语言:txt
复制
- Tshared实现原理
    - ObjectType\* Object - 裸指针
    - FReferenceControllerBase - 引用计数器
        - FReferenceControllerBase是抽象类, 子类需要实现DestroyObject()方法. (目前均使用派生类TReferenceControllerWithDeleter) SharedReferenceCount为共享计数, WeakReferenceCount为弱指针计数. SharedReferenceCount降为0时, 不管WeakReferenceCount的数值, Object被销毁. WeakReferenceCount降为0时, FReferenceControllerBase 被销毁.

##详细内容

0. 总述


0.1. FReferenceControllerBase - 引用计数器

代码语言:txt
复制
TSharedPtr, TSharedRef, TWeakPtr均使用该类计数
FReferenceControllerBase是抽象类, 子类需要实现DestroyObject()方法. (目前均使用派生类TReferenceControllerWithDeleter)
SharedReferenceCount为共享计数, WeakReferenceCount为弱指针计数.
SharedReferenceCount降为 0 时, 不管WeakReferenceCount的数值, Object被销毁.
WeakReferenceCount降为 0 时, FReferenceControllerBase 被销毁.

0.2. TSharedPtr - 共享指针

代码语言:txt
复制
不可用于UObject, 允许为null, 带引用计数, 生命周期完结自释放的智能指针, 允许拷贝多份, 每份均指向相同的对象.
使用右值构造/赋值时, 传入智能指针会被释放.
有线程安全非线程安全两个版本
每复制一份, 引用计数+1, 每释放一份, 引用计数-1.

0.3. TSharedRef - 共享引用

代码语言:txt
复制
不可用于UObject, 不允许为null, 带引用计数, 生命周期完结自释放的智能指针, 允许拷贝多份, 每份均指向相同的对象.
与TSharedRef的区别仅为不允许为null.
TSharedPrt::ToSharedRef()方法用于创建一个共享引用.

0.4. TWeakPtr - 弱指针

代码语言:txt
复制
仅能通过TSharedPrt或TSharedRef构造的弱指针, 随时会变成空指针, 使用前必须校验是否为空.
与TSharedPrt或TSharedRef使用同一个FReferenceControllerBase.
TWeakPtr无法直接访问对象, 必须通过Pin()方法创建一个TSharedPtr, 通过TSharedPtr访问对象.
弱指针计数不会阻止对象销毁

0.5. TScopedPointer - 自释放指针

代码语言:txt
复制
boost::scoped_ptr
生命周期完结后自释放的智能指针(构造的裸指针必须是new出来的).
拷贝构造/赋值时会new一个新的对象.
手动释放(Release)后, 该智能指针不再负责该对象的销毁.

0.6. TAutoPtr - 无副本自释放指针

代码语言:txt
复制
与TScopedPointer功能一致, 唯一的区别是拷贝构造时不会new.

0.7.TUniquePtr - 唯一指针

代码语言:txt
复制
std::unique_ptr
具有唯一性, 生命周期完结后自释放的智能指针, 不支持拷贝, 只支持移动.
使用右值赋值时, 原指向对象会被销毁.
手动释放(Release)后, 该智能指针不再负责该对象的销毁.

0.8. FWeakObjectPtr - UObject 弱指针

代码语言:txt
复制
使用ObjectIndex及ObjectSerialNumber指向UObject的弱指针.

0.9 TSharedFromThis

提供AsShared方法获取共享指针, 凡是派生自TSharedFromThis的对象请通过AsShared获取智能指针, 可以保证引用计数器的一致.

1. TSharedPtr - 共享指针


代码语言:txt
复制
使用引用计数的允许为空的智能指针, 创建时有三种模式(ESPMode)可选:
NotThreadSafe- 非线程安全
Fast(默认) - FORCE_THREADSAFE_SHAREDPTRS为 1 是等价于ThreadSafe, 反之等价于NotThreadSafe
ThreadSafe - 线程安全, 但是相对昂贵

注: TSharedRef 不允许用于UObjects

1.1 TSharedPtr

1.1.1 TSharedPtr 的结构

代码语言:txt
复制
template< class ObjectType, ESPMode Mode >
ObjectType* Object - 裸指针
SharedPointerInternals::FSharedReferencer< Mode > SharedReferenceCount - 引用计数器 (详见1.2)

1.1.2 TSharedPtr 的关键方法

代码语言:txt
复制
ObjectType& Get() const
获取裸指针, 请慎重存储返回值.

const int32 GetSharedReferenceCount() const
获取当前指针的引用计数

void Reset()
释放引用

TSharedRef< ObjectType, Mode > ToSharedRef() const
创建一个共享引用

const bool IsValid() const
当前指针是否有效

1.2 SharedPointerInternals::FSharedReferencer

template< ESPMode Mode >

1.2.1 SharedPointerInternals::FSharedReferencer 的结构

代码语言:txt
复制
typedef FReferenceControllerOps<Mode> TOps;
FReferenceControllerBase* ReferenceController
引用计数核心, 负责记录引用计数及销毁操作 (详见1.3)
注: FReferenceControllerBase是一个抽象类, 目前TSharedRef使用的ReferenceController是TReferenceControllerWithDeleter

1.2.2 SharedPointerInternals::FSharedReferencer 的关键方法

代码语言:txt
复制
FSharedReferencer& operator=( FSharedReferencer const& InSharedReference )
      TOps::AddSharedReference(NewReferenceController);
         增加ReferenceController->SharedReferenceCount计数, 线程安全版使用InterlockedIncrement, 非线程安全版直接++
      TOps::ReleaseSharedReference(ReferenceController)
         减少ReferenceController->SharedReferenceCount计数, 线程安全版使用InterlockedDecrement, 非线程安全版直接--
         若ReferenceController->SharedReferenceCount为 0
             ReferenceController->DestroyObject()进行销毁
             ReleaseWeakReference(ReferenceController)
                  若WeakReferenceCount为0, delete ReferenceController

FORCEINLINE ~FSharedReferencer()
     TOps::ReleaseSharedReference(ReferenceController);

1.3. FReferenceControllerBase

1.3.1 FReferenceControllerBase 的结构

代码语言:txt
复制
int32 SharedReferenceCount - 共享引用计数, 该计数为 0 时会销毁Object , 即便WeakReferenceCount不为 0
int32 WeakReferenceCount - 弱指针引用计数

1.3.2 FReferenceControllerBase 的关键方法

代码语言:txt
复制
FReferenceControllerBase( FReferenceControllerBase const& )
     无定义, private, 隐藏左值拷贝构造
FReferenceControllerBase& operator=( FReferenceControllerBase const& )
     无定义, private, 隐藏左值赋值

1.4. TReferenceControllerWithDeleter

1.4.1 TReferenceControllerWithDeleter 的结构

代码语言:txt
复制
template <typename ObjectType, typename DeleterType>
void* Object - 裸指针
继承自FReferenceControllerBase, 目前是FReferenceControllerBase的派生类 

1.4.2 TReferenceControllerWithDeleter 的关键方法

virtual void DestroyObject()

使用DeleterType的operator()方法来释放Object

代码语言:txt
复制
DeleterType通过DefaultDeleter<ObjectType>()创建
DefaultDeleter通过模板特化实现不同类型的delete, 默认直接使用delete.

1. 5 使用TSharedPtr

1.5.1 创建新的 TSharedPtr

代码语言:txt
复制
SharedPtr( OtherType* InObject )
      this->Object( InObject )
      this->SharedReferenceCount( SharedPointerInternals::NewDefaultReferenceController( InObject ) )
           this->SharedReferenceCount.ReferenceController( NewDefaultReferenceController )
           NewDefaultReferenceController = nullptr;
      SharedPointerInternals::EnableSharedFromThis( this, InObject, InObject );

1.5.2 复制复制 TSharedPtr

1.5.2.1 使用左值使用 TSharedPtr

代码语言:txt
复制
TSharedPtr( TSharedPtr const& InSharedPtr )
     this->Object( InSharedPtr.Object )
     this->SharedReferenceCount( InSharedPtr.SharedReferenceCount )
          this->SharedReferenceCount.ReferenceController( InSharedPtr.SharedReferenceCount.ReferenceController )
          TOps::AddSharedReference(this->SharedReferenceCount.ReferenceController);

1.5.2.2 使用右值构造 TSharedPtr

代码语言:txt
复制
TSharedPtr( TSharedPtr&& InSharedPtr )
     this->Object( InSharedPtr.Object )
     this->SharedReferenceCount( MoveTemp(InSharedPtr.SharedReferenceCount) )
          this->SharedReferenceCount.ReferenceController( InSharedReference.ReferenceController )
          InSharedPtr.SharedReferenceCount.ReferenceController = nullptr;
     InSharedPtr.Object = nullptr;

1.5.2.3 使用左值赋值TSharedPtr

代码语言:txt
复制
TSharedPtr& operator=( TSharedPtr const& InSharedPtr )
     this->SharedReferenceCount = InSharedPtr.SharedReferenceCount;
          this->SharedReferenceCount.ReferenceController( InSharedPtr.SharedReferenceCount.ReferenceController )
          TOps::AddSharedReference(this->SharedReferenceCount.ReferenceController);
     this->Object = InSharedPtr.Object;
     return *this;

1.5.2.3 使用右值赋值 TSharedPtr

代码语言:txt
复制
TSharedPtr& operator=( TSharedPtr&& InSharedPtr )
      this->Object = InSharedPtr.Object;
      InSharedPtr.Object = nullptr;
      this->SharedReferenceCount = MoveTemp(InSharedPtr.SharedReferenceCount);
           auto OldReferenceController=this->SharedReferenceCount.ReferenceController;
           InSharedPtr.SharedReferenceCount.ReferenceController = nullptr;
           this->SharedReferenceCount.ReferenceController = NewReferenceController;
           TOps::ReleaseSharedReference(OldReferenceController);
      return *this;

2. TSharedRef - 共享引用


使用引用计数的不允许为空的智能指针.

相关逻辑与TSharedPtr相同, 仅不允许Object为空.

2.1 TSharedRef 的结构

代码语言:txt
复制
ObjectType* Object
SharedPointerInternals::FSharedReferencer< Mode > SharedReferenceCount

2.2 TSharedRef 的关键方法

详见1.1.

3. TWeakPtr - 弱指针


代码语言:txt
复制
使用引用计数的智能指针, 弱指针不会阻止对象销毁, 使用过程中会遇到指针突然变空的情况.

TWeakPtr不能通过裸指针构造, 只能通过其他智能指针构造.

3.1 TWeakPtr

3.1.1 TWeakPtr 的结构

代码语言:txt
复制
ObjectType* Object - 裸指针
SharedPointerInternals::FWeakReferencer< Mode > WeakReferenceCount - 引用计数器

3.1.2 TWeakPtr 的关键方法

代码语言:txt
复制
const bool IsValid() const
return Object != nullptr && WeakReferenceCount.IsValid();
如前文所说, 弱指针并不阻止Object销毁, 故TWeakPtr::Object并不能作为是否有效的条件, 需要使用WeakReferenceCount.IsValid()来判断.

void Reset()
释放引用

TSharedPtr< ObjectType, Mode > Pin() const
获取当前弱指针的共享指针, 请务必记得IsValid判断.
弱指针没有Get方法, 也没有重写operator*及operator->, 想要使用弱指针指向的对象, 必须通过Pin方法获取共享指针.

3.2 FWeakReferencer

typedef FReferenceControllerOps<Mode> TOps;

3.2.1 FWeakReferencer 的结构

FReferenceControllerBase* ReferenceController

3.2.2 FWeakReferencer 的关键方法

代码语言:txt
复制
FSharedReferencer& operator=( FSharedReferencer const& InSharedReference )
InSharedReference WeakReference + 1
this WeakReference - 1
this = InSharedReference
~FWeakReferencer()
TOps::ReleaseWeakReference(ReferenceController);
WeakReferenceCount为 0 时销毁FReferenceController

4. TScopedPointer - 自释放指针


仿照boost::scoped_ptr写的指针, 没有引用计数, 生命周期完结时自动delete.

4.1 TScopedPointer 的结构

ReferencedType* Reference - 裸指针

4.2 TScopedPointer 的关键方法

代码语言:txt
复制
TScopedPointer(ReferencedType* InReference = nullptr)
TScopedPointer(const TScopedPointer& InCopy)

拷贝构造时, 使用new创建新的对象, InCopy 与与 this 指向不同的对象指向不同的对象.

代码语言:txt
复制
~TScopedPointer()
ReferencedType* GetOwnedPointer() const
获取裸指针

ReferencedType* Release()
释放该指针, 并返回裸指针. 调用后TScopedPointer不再负责该指针的销毁.

5. TAutoPtr - 无副本自释放指针


与TScopedPointer功能一致, 唯一的区是构造时别不会new新的对象.

5.1 TAutoPtr 的结构

ReferencedType* Reference - 裸指针

5.2TAutoPtr 的关键方法

TAutoPtr(ReferencedType* InReference = nullptr)

~TAutoPtr()

6. TUniquePtr - 唯一指针

仿照std::unique_ptr写的指针, 具有唯一性, 不支持拷贝, 只支持移动.

私有继承自Deleter, 通过static_cast<Deleter&>(*this)执行销毁操作.

6.1 TUniquePtr 的结构


T* Ptr - 裸指针

6.2 TUniquePtr 的关键方法

代码语言:txt
复制
private TUniquePtr(const TUniquePtr&) - 禁止拷贝
代码语言:txt
复制
private UniquePtr& operator=(const TUniquePtr&) - 禁止拷贝
TUniquePtr(TUniquePtr&& Other)
使用右值构造, 构造后Other的指针被置空.

TUniquePtr& operator=(TUniquePtr&& Other)
使用右值赋值, 赋值后Other的指针被置空, this 原来指向的对象被销毁原来指向的对象被销毁.

~TUniquePtr()
T* Release()
释放该指针, 并返回裸指针. 调用后TUniquePtr不再负责该指针的销毁.

7. FWeakObjectPtr - 针对UObject的弱指针


代码语言:txt
复制
支持指向UObject的弱指针, 本身并不包含裸指针, 通过UObject的Index来指向对象.

7.1 FWeakObjectPtr 的结构

代码语言:txt
复制
int32 ObjectIndex - 指向UObject的ObjectIndex

int32 ObjectSerialNumber - 指向UObject的ObjectSerialNumber

7.2 FWeakObjectPtr 的关键方法

代码语言:txt
复制
FWeakObjectPtr(const class UObject *Object)
FWeakObjectPtr(const FWeakObjectPtr &Other)
UObject* FWeakObjectPtr::Get(bool bEvenIfPendingKill) const
获取当前指向的UObject
代码语言:txt
复制
UObject* FWeakObjectPtr::GetEvenIfUnreachable() const
获取当前指向的UObject(RF_PendingKill | RF_Unreachable)

UObject* FWeakObjectPtr::Get() const
获取当前指向的UObject(非RF_PendingKill )

void Reset()
重置ObjectIndex及ObjectSerialNumber

8. TSharedFromThis


代码语言:txt
复制
 为派生自TSharedFromThis的对象提供AsShared方法, 无论何时调用均可获得相同FReferenceControllerBase的智能指针.

8.1 TSharedFromThis 的结构

代码语言:txt
复制
 mutable TWeakPtr< ObjectType, Mode > WeakThis - 指向this的弱指针

8.2 TSharedFromThis 的关键方法

代码语言:txt
复制
TSharedRef< ObjectType, Mode > AsShared()
将WeakThis转换成TSharedPtr并返回

void UpdateWeakReferenceInternal( TSharedPtr< SharedPtrType, Mode > const* InSharedPtr, OtherType* InObject ) const
在该对象第一次转化成TSharedPtr或TSharedRef时通过EnableSharedFromThis触发, 创建WeakThis弱指针.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是智能指针
  • 使用智能指针
    • 0. 总述
      • 0.1. FReferenceControllerBase - 引用计数器
        • 0.2. TSharedPtr - 共享指针
          • 0.3. TSharedRef - 共享引用
            • 0.4. TWeakPtr - 弱指针
              • 0.5. TScopedPointer - 自释放指针
                • 0.6. TAutoPtr - 无副本自释放指针
                  • 0.7.TUniquePtr - 唯一指针
                    • 0.8. FWeakObjectPtr - UObject 弱指针
                      • 0.9 TSharedFromThis
                        • 1. TSharedPtr - 共享指针
                          • 1.1 TSharedPtr
                            • 1.1.1 TSharedPtr 的结构
                              • 1.1.2 TSharedPtr 的关键方法
                                • 1.2 SharedPointerInternals::FSharedReferencer
                                  • 1.2.1 SharedPointerInternals::FSharedReferencer 的结构
                                    • 1.2.2 SharedPointerInternals::FSharedReferencer 的关键方法
                                      • 1.3. FReferenceControllerBase
                                        • 1.3.1 FReferenceControllerBase 的结构
                                          • 1.3.2 FReferenceControllerBase 的关键方法
                                            • 1.4. TReferenceControllerWithDeleter
                                              • 1.4.1 TReferenceControllerWithDeleter 的结构
                                                • 1.4.2 TReferenceControllerWithDeleter 的关键方法
                                                  • 1. 5 使用TSharedPtr
                                                    • 1.5.1 创建新的 TSharedPtr
                                                      • 1.5.2 复制复制 TSharedPtr
                                                        • 1.5.2.1 使用左值使用 TSharedPtr
                                                          • 1.5.2.2 使用右值构造 TSharedPtr
                                                            • 1.5.2.3 使用左值赋值TSharedPtr
                                                              • 1.5.2.3 使用右值赋值 TSharedPtr
                                                                • 2. TSharedRef - 共享引用
                                                                  • 2.1 TSharedRef 的结构
                                                                    • 2.2 TSharedRef 的关键方法
                                                                      • 详见1.1.
                                                                    • 3. TWeakPtr - 弱指针
                                                                      • 3.1 TWeakPtr
                                                                        • 3.1.1 TWeakPtr 的结构
                                                                          • 3.1.2 TWeakPtr 的关键方法
                                                                            • 3.2 FWeakReferencer
                                                                              • 3.2.1 FWeakReferencer 的结构
                                                                                • 3.2.2 FWeakReferencer 的关键方法
                                                                                  • 4. TScopedPointer - 自释放指针
                                                                                    • 4.1 TScopedPointer 的结构
                                                                                      • 4.2 TScopedPointer 的关键方法
                                                                                        • 5. TAutoPtr - 无副本自释放指针
                                                                                          • 5.1 TAutoPtr 的结构
                                                                                            • 5.2TAutoPtr 的关键方法
                                                                                              • 6. TUniquePtr - 唯一指针
                                                                                                • 6.1 TUniquePtr 的结构
                                                                                                  • 6.2 TUniquePtr 的关键方法
                                                                                                    • 7. FWeakObjectPtr - 针对UObject的弱指针
                                                                                                      • 7.1 FWeakObjectPtr 的结构
                                                                                                        • 7.2 FWeakObjectPtr 的关键方法
                                                                                                          • 8. TSharedFromThis
                                                                                                            • 8.1 TSharedFromThis 的结构
                                                                                                              • 8.2 TSharedFromThis 的关键方法
                                                                                                              相关产品与服务
                                                                                                              容器服务
                                                                                                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                                                                                              领券
                                                                                                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档