UE5 C++中的弱指针:概念、使用方法与注意事项
UE5 C++中的弱指针:概念、使用方法与注意事项
弱指针(TWeakPtr)是UE5 C++中的一种智能指针类型,主要用于解决循环引用问题以及在不需要强引用保证对象始终有效的场景下,提供一种可以获取对象访问权限的方式。本文将详细介绍弱指针的概念、使用方法和注意事项。
概念
在UE C++ 中,弱指针(TWeakPtr)也是一种智能指针类型,主要用于解决循环引用问题以及在不需要强引用保证对象始终有效的场景下,提供一种可以获取对象访问权限的方式。与共享指针(TSharedPtr)和共享引用(TSharedRef)不同,弱指针不会增加其所指向对象的引用计数,这意味着它不会对对象的生命周期产生维持作用,即不会阻止对象被销毁。
例如,在一些复杂的对象关系结构中,多个对象之间可能相互引用,如果都使用强引用(如TSharedPtr或TSharedRef),很容易形成循环引用,导致对象的引用计数永远无法降为 0,从而造成内存泄漏。而弱指针可以在这种情况下参与对象的引用关系构建,避免出现循环引用问题,同时又能在对象仍然存在时,有机会获取到对象的有效访问权限。
在访问弱指针引用的对象前,应使用Pin函数生成共享指针。此操作确保使用该对象时其将继续存在。如只需要确定弱指针是否引用对象,可将其与nullptr比较,或在之上调用IsValid。
声明和初始化
在如下代码中,体现了弱指针不维持对象生命周期的特点,以及通过Pin函数检查对象是否还能获取有效访问权限的用法。
在第16行代码中,使用MakeShared
第17行代码通过将ObjectOwnerRef作为参数传递给TWeakPtr的构造函数,创建了一个弱指针ObjectObserver,使其指向与ObjectOwnerRef相同的FMyStruct对象。需要注意的是,这个操作并不会增加对象的引用计数,对象的生命周期仍然仅由ObjectOwnerRef(以及后续可能出现的其他指向该对象的共享指针或共享引用)来维持,ObjectObserver只是建立了一个对该对象的弱引用关系,用于后续在不影响对象生命周期的情况下尝试获取对对象的访问权限。
第18行代码创建了一个TSharedPtr类型的共享指针ObjectOwnerPtr,并通过赋值操作让它也指向ObjectOwnerRef所指向的FMyStruct对象。此时,对象的引用计数会从1(仅由ObjectOwnerRef维持时)变为2。
第19行代码调用ObjectOwnerPtr的Reset函数,这会使得ObjectOwnerPtr释放对其所指向对象的强引用,对象的引用计数会相应地减1。在执行完这行代码后,对象的引用计数变回1,仅由ObjectOwnerRef来维持其生命周期。
第20~23行代码使用了弱指针ObjectObserver的Pin函数来尝试获取一个指向原对象的临时共享指针,以检查对象是否仍然可以被访问。Pin函数会在对象仍然存在(即对应的引用计数大于0)的情况下,返回一个指向该对象的临时TSharedPtr。
转换为共享指针
Pin函数将创建指向弱指针对象的共享指针。只要共享指针在范围内且引用对象,则该对象将持续有效。
如下代码主要展示了如何将一个由共享引用(TSharedRef)管理的对象转换为可通过弱指针(TWeakPtr)来间接访问的形式,并且演示了通过弱指针的Pin操作获取临时共享指针(TSharedPtr),进而访问对象成员函数PrintAA的过程,整体体现了弱指针在不影响对象生命周期管理的情况下,实现对对象的安全访问机制。
打破循环引用
出现循环引用的示例:
首先在“FMyStruct”结构体中定义一个共享指针HoldPtr,并初始化为nullptr然后创建两个FMyStruct类型的对象,并通过它们各自包含的TSharedPtr
此时调用“LoopPtr”会发现,并没有输出析构的日志信息,说明产生了循环引用现象,导致对象的引用计数永远无法降为 0。
为了打破循环引用,我们可以使用弱指针来代替共享指针
编译后运行 结果如下,可以看到对象可以正常析构了:
弱指针使用警告
如不想保证数据对象会持续存在时,弱指针将非常有用,但该属性可能会变得异常危险。在以下情况中请谨慎使用弱指针:
- 在Set或Map中用作键。弱指针可能会在未通知容器的情况下随时无效,因此共享指针或共享引用更适用于充当键。可安全地将弱指针用作数值。
- 虽然弱指针提供IsValid函数,但是检查IsValid无法保证对象在任何时间长度内均可持续有效。线程安全共享指针可能会因另一线程上的活动而随时无效,因此使用线程安全共享指针应尤其注意。
Pin返回的共享指针将使对象在代码将其清除或其超出范围前保持活跃状态,因此Pin函数是用于检查的首选方法,此类检查会导致取消引用或访问存储对象。