[UE4]共享指针与共享引用的理解和使用

文章讲述了在C++编程中,尤其是使用虚幻引擎时,如何通过智能指针(如共享指针、共享引用、弱指针)解决内存泄漏问题。虚幻引擎引入这些智能工具,使得动态构建对象时不再需要手动释放内存,同时介绍了如何在自定义类中管理U类对象指针,通过继承FGCObject并重写AddReferencedObjects函数来阻止垃圾回收器过早释放对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

指针

        C++中,往往令人头痛的是指针的管理问题!在对象动态构建时,我们需要将对象指针进行存储,一旦忘记释放,那么将会导致不可预估的错误。在C++中排查指针导致的内存泄漏问题实在令人头痛!在虚幻中,为了解决此类问题,加入了智能指针(共享指针,共享引用,弱指针),当我们使用动态方式构建对象时,再也不需要担心内存释放问题!指针的释放规则由引擎制定,包括释放时机

自定义类

        在构建自定义类时,我们经常遇到一种情况,当类中持有U类对象指针时,我们希望阻止垃圾回收器对对象释放。但是自定义类中又无法使用UPROPERTY宏,那么我们可以采取将类继承自FGCObject,并重写父类函数AddReferencedObjects。将需要阻止释放的指针加入到操作队列,以防止对象被垃圾回收器回收!

        注意:当构建类被释放时(需要我们保证),并且调用其析构函数(析构函数需要重写父类析构函数),对象将自动清除其所添加的所有引用。

//创建自定义类
Class FCustomClass
{
public:
    
     FCustomClass();
     
     ~ FCustomClass();
     
    void CallFunction();

};
智能指针

       虚幻中存在一套非常强大的动态内穿管理机制,而这套机制中根本在智能指针(非侵入式),并且UE的智能指针速度相比STL更快,速度的普通C++指针速度一样。智能指针本质的目的是将释放内存工作进行托管。当两个智能指针指向同一空间,一个设置为空,另一个不会跟随为空,智能指针设置为空并不是释放内存空间,只是减少空间的引用。

        注意:智能指针只能用于自定义类,U类禁止使用

优点

描述

简洁的语法

可以像操作常规的C++指针那样来复制、解引用及比较共享指针。

防止内存泄露

当没有共享引用时资源自动销毁。

弱引用

弱指针允许您安全地检查一个对象是否已经被销毁。

线程安全

包含了可以通过多个线程安全地进行访问的“线程安全”版本。

普遍性

几乎可以创建到任何类型的对象的共享指针

运行时安全

共享指针永远不会为null,且总是可以解引用

不会产生循环引用

使用弱引用来断开引用循环

表明用途

可以轻松区分对象 拥有者和观察者

性能

共享指针的性能消耗最小,所有操作所占时间都是固定的

强大的功能

支持const、前置声明的不完全类型、类型转换等

内存

所占内存大小是C++指针在64位系统中所占内存的两倍(外加了一个共享的16字节的引用控制器)

共享指针(TSharePtr)引用计数的非侵入式的权威智能指针

        共享指针式虚幻中最常见的智能指针,在操作上可以帮助我们构建托管内存指针!共享指针本身非侵入式的,这使得指针的使用与操作和普通指针一致!共享指针支持主动指向空,并且共享指针是线程安全的,节省内存,性能高效。

        注意:构建自定义类时,需要使用F开头命名

构建自定义类

 基本语法

//创建共享指针
TSharedPtr<FCustomClass> SharedPtr;//创建一个共享指针,但是没有维护任何内存控件
TSharedPtr<FCustomClass> SharedPtr(new FCustomClass());//创建一个共享指针,并维护了一块内存空间
TSharedPtr<FCustomClass> SharedPtr = MakeShareable(new FCustomClass());//创建一个共享指针,并维护了一块内存空间,MakeShareable() 该函数是用来构建共享指针的快捷方式


//解引用和操作共享指针
//注意:调用成员内容建议使用第一种方法,取原生指针建议使用第二种方法
//CallFun()为FCustomClass成员函数
SharedPtr->CallFun();//通过共享指针直接调用对象成员函数
SharedPtr.Get()->CallFun();//Get()返回值为共享指针所引用的对象,如果没有引用对象则返回nullptr
(*SharedPtr).CallFun();//解引用共享指针,获取共享指针所引用对象地址

//比较共享指针
TSharedPtr<FCustomClass> SharedPtr1(new FCustomClass);
TSharedPtr<FCustomClass> SharedPtr1 = MakeShareable(new FCustomClass());
if(SharedPtr1 == SharedPtr2)//比较两个智能指针是否管理内存同一块内存
{    
}

//判断共享指针的有效性
TSharedPtr<FCustomClass> SharedPtr = MakeShareable(new FCustomClass());
if(SharedPtr.IsValid())//判断共享指针是否为空,注意操作的函数是共享指针的成员函数
{    
}
if(SharedPtr.Get() != nullptr)//判断共享指针是否为空,注意操作的函数是共享指针的成员函数
{            
}

//释放智能指针(正常情况下智能指针不需要主动释放)
TSharedPtr<FCustomClass> SharePtr = MakeShareable(new FCustomClass());
SharedPtr.Reset();
SharedPtr = nullptr;

//获取引用计数器
SharedPtr.GetSharedReferenceCount();//该函数会返回FCoustomClass的引用数 
共享引用(TShareRef)不能设置null值的、引用计数的、非侵入式权威智能指针
//创建共享引用
TSharedRef<FCustomClass> SharedRef(new FCustomClass);

//共享指针转为共享引用,更改引用对象
SharedRef = SharedPtr.ToSharedRef();

//共享引用转为共享指针
SharedPtr = SharedRef;
弱指针(TWeakPtr)引用计数的、非侵入式弱指针引用

        弱指针不会阻止对象的销毁,如果引用对象被销毁,则弱指针也将被清空。一般弱指针的操作意图是保存了一个到达目标对象的的指针,但不会控制该对象的生命周期,弱指针不会增加引用计数,可以用来断开引用循环问题

        无论谁销毁了对象,只要其对象被销毁,弱指针都将被清空。

声明和初始化

弱指针无法直接申请内存空间,需要借助共享引用或共享指针为弱指针赋值

//声明空的弱指针
TWeakPtr<FCustomClass> WeakPtr;

//借助共享指针构建弱指针
TSharedPtr<FCustomClass> SharedPtr;
TWeakPtr<FCustomClass> WeakPtr(SharedPtr);

//借助共享引用构建弱指针
TSharedRef<FCustomClass> SharedRef;
TWeakPtr<FCustomClass> WeakPtr(SharedRef); 

解引用操作

//弱指针无法直接调用(直接调用弱指针会出现对象为空,因为弱指针不会阻止对象的释放)
//调用弱指针,需要把弱指针转换为共享指针
//pin()函数会阻止对象被销毁

TSharedPtr<FCustomClass> SharedPtr(new FCustomClass);//声明共享指针并赋值
TWeakPtr<FCustomClass> WeakPtr(SharedPtr);//创建一个弱指针并赋值

//调用弱指针
//语法一
TSharedPtr<FCustomClass> WeakToSharedPtr(WeakPtr.Pin());//将弱指针转换为共享指针
if(TWeakPtr)
{
    WeakToSharedPtr.CallFunction();
}
//检查转换是否成功
//语法二
TSharedPtr<FCustomClass> WeakToSharedPtr = WeakPtr.Pin();//将弱指针赋值给给共享指针
WeakToSharedPtr.CallFunction();

检查弱指针有效性

if(WeakPtr.IsValid())
{    
}

释放弱指针(弱指针不影响引用计数,一般不需要主动释放)

WeakPtr = nullptr;
在自定义类中如何管理U类指针和自定义类型指针

        在自定义类型中声明另一个自定义类型指针时,必须使用共享指针或者弱指针,不得使用共享引用(共享引用无法作为成员变量存在);在自定义类型中声明U类型指针时,需要对U类指针进行关联托管(防止U类指针被其他对象释放)。

托管U类指针前提:

        1.自定义类声明时使用共享指针;

        2.自定义类需要继承 "FGCObject" 类

//.h
class UECPP_API FCustomClass : Class FGCObject
{
public:
    FCustomClass();
    virtual ~FCustomClass();
    
    //FCoustom和则其他对象在执行析构函数时会调用该函数
    //该函数会在垃圾回收其中将本类关联的U类指针标记为强引用,只有当本类被释放时,关联的U类指针才会被回收
    virtual void AddReferencedObjects(FReferenceCollector& Collector) overrider;    
protected:    
    class UObject* P_Uobject;    
};

//.cpp
void  FCustomClass::AddReferencedObjects(FReferenceCollector& Collector)
{
    Collector.AddReferencedObjects(P_Uobject);//将指针在垃圾回收器中标记为强引用,无法被回收
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值