UE4]智能指针

UE4 智能指针

为什么要用这个东西

主要是方便管理内存的释放,使用智能指针可以通过引用计数的方式方便的处理这些问题。

UE的智能指针来源于c++的智能指针,同时针对UE引擎的对接进行了封装,尤其是在TArray里使用智能指针更便捷,高性能。

四种指针

基本结构
xxx<class OjectType, ESPMode Mode>

类型名称对应C++
共享指针TSharedPtrstd::shared_ptr
共享引用TSharedRefstd::shared_ref
弱指针TWeakPtrstd::weak_ptr
自带弱引用TSharedFromThisstd::enable_shared_from_this

OjectType 对象类型
ESPMode 指针模式:更快线程安全

{
    NotThreadSafe=0, // 最快
    Fast=FORCE_THREADSAFE_SHAREDPTRS?1:0,// 用宏来控制
    ThreadSafe=1 // 有多线程必用
}

在创建时,可以不填写ESPMode,默认为ESPMode::Fast

创建共享指针

//创建共享指针
TSharedPtr<FTestClass> TestPtr = MakeShareable(new FTestClass);

// 是否有效
TestPtr.IsValid();

// 引用计数
TestPtr.GetSharedReferenceCount();

// 解引用
FTestClass* TestClass =  TestPtr.Get();

// 手动释放()
// 只有共享指针释放功能,共享引用没有,因为引用不能为空,到少为1。
TestPtr = nullptr;
TestPtr.Reset();

//看引用是否唯一,用在类似单例的情况下
TestRef.IsUnique();

创建共享引用

❗ 注意: 引用不能为空
❗ 注意: 引用不能为空
❗ 注意: 引用不能为空

//创建共享引用 隐匿转换
TSharedRef<FTestClass> TestRef = MakeShareable(new FTestClass);

// 错误的共享引用创建,因为引用不能为空
// TSharedRef<FTestClass> TestRef;
// TSharedRef<FTestClass> TestRef = nullptr;


TestPtr.IsValid();// 是否有效
TestPtr.GetSharedReferenceCount();// 引用计数
FTestClass* TestClass =  TestPtr.Get();// 解引用
TestRef.IsUnique();//是否唯一

// 不能手动释放
// 不能手动释放
// 不能手动释放

一个小知识点

c++ 在 new 的时候会生成一个对象指针

FTestClass* TestClass = new TestClass();

new也分两种

new TestClass();
new TestClass;

大部分情况没区别,只有一种情况有区别:

  • new class 动态申请的空间里面的值是随机值
  • new class() 进行了内置类型值的初始化,里面的值为0来

TSharedPtr与TSharedRef互相转换


// 引用 -> 指针 直接转,隐式转换
TSharedPtr<FTestClass> TestPtr2 = TestRef;

// 指针 -> 引用
TSharedRef<FTestClass> TestRef2 = TestPtr.ToSharedRef();

父子类转换

获取到一个对象,在统一处理时,会按基类(父类)来处理,但到某个时候想要转换成子类时,智能指针需要特殊转换。

如有父类FParentClass和子类FSonClass


// 父类转子类 需要调用方法
TSharedPtr<FParentClass> ParentPtr = MakeShareable(new FSonClass);
TSharedPtr<FSonClass> SonPtr = StaticCastSharedPtr<FSonClass> (ParentPtr);
if(SonPtr.IsValid())
{
	// 如果转换成功就可以调用子类相关的函数
}

// 子类转父类 直接转换

// 引用同理
StaticCastSharedRef<>();

常量类型消除常量

有时对一个const的对象进行智能指针封装,有时需要消除他的const属性,来调用函数,可以如下。

TSharedPtr< const FBaseClass> ConstBaseClass = MakeShareable(new FBaseClass());
if (ConstBaseClass.IsValid())
{
	ConstBaseClass->ConstBaseFun(); // ConstBaseFun为const函数
	// ConstBaseClass->BaseFun(); // BaseFun为非const函数,错误,因为为const对象不能访问非const函数
	TSharedPtr<FBaseClass> BaseClass = ConstCastSharedPtr<FBaseClass>(ConstBaseClass);
	if (BaseClass.IsValid())
	{
		BaseClass->BaseFun();
	}
}

// 引用同理
ConstCastSharedRef<>();

弱指针

在出现环形引用的时候,如果都使用强指针,则永远不会销毁。

class FClassA:
{
public:
	TSharedPtr<FClassB> ClassBRef;
}
class FClassB:
{
public:
	TSharedPtr<FClassA> ClassARef;
}

FClassA里引用FClassB,同理FClassB也引用了FClassA,此时他们的引用技术为都为1,即便不使用了,也不会变为0而销毁。

如何解决:

不管哪个类里,如在FClassA中,把TSharedPtr<FClassB>修改为TWeakPtr<FClassB>,弱指针只保留引用,并不计数,所以就能解决这个问题。但也同时需要注意,有可能弱指针的引用无效,所以使用前都要很检查下。

常用用法:

  1. 弱指针不能影响指针的引用计数。
  2. 在使用弱指针时,需要对其进行检测,判断其是否有效。
  3. 弱指针最大的用途就是解决环形引用的问题。
  4. 弱指针另一个作用就是,不对资源进行竞争。只能使用资源,却没有权限控制资源的生命周期。

让类自带弱引用

我们在处理一个原生类时,如果原生类想与其他智能对象有交互,可能让这个原生类继承TSharedFromThis,这样在使用这个对象时,可以自带智能指针。

class FTestClass : TSharedFromThis<FTestClass>
{}

FTestClass* TestClass = new FTestClass();

// 如果这时想当智能指针用,要么
TSharedPtr<FTestClass> TestPtr = MakeShareable(new FTestClass());

// 还可以这样
TestNormalPtr->AsShared();

这样可以灵活的使用智能指针。
其实最开始c++enable_shared_from_this目的主要是使用在多线程中,比如Socket通信时。

最后注意

UE的UObject自带垃圾回收,使用的就是智能指针,所以,不能对UObject再次使用智能指针。

评论