UEC++ 智能指针——共享指针、共享引用、弱指针

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

1、自定义类

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

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

2、智能指针

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

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

共享指针和共享引用的优点:

三个指针

 3、共享指针

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

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

 

 基本操作语法:

// 自定义类
class MX_API FTestClass
{
public:
    FTestClass();
    ~FTestClass();

    void TestFun();
};
    
   // 其他类
  // 构建一个共享指针,但是没有维护任何空间 TSharedPtr<FTestClass> Ftc01;// 不推荐 // 构建一个共享指针,并维护一快内存 TSharedPtr<FTestClass> Ftc02(new FTestClass()); //MakeShareable函数是用来构建共享指针的快捷方式 // 构建一个共享指针,并维护一快内存 TSharedPtr<FTestClass> Ftc03 = MakeShareable(new FTestClass()); // 解引用和操作 Ftc02->TestFun(); Ftc02.Get()->TestFun(); (*Ftc02).TestFun(); // 比较两个智能管理的内存是否是同一个 if (Ftc02 == Ftc03) {} // 判断是否为空 注意操作函数是共享指针的成员函数 if (Ftc02.IsValid()) {} // 注意操作函数是共享指针的成员函数 if (Ftc02.Get() == nullptr) {} // 获取引用计时器 获得当前地址被引用个数 Ftc02.GetSharedReferenceCount(); // 释放 Ftc02.Reset(); Ftc03 = nullptr;

 4、共享引用

  • 共享引用禁止为空,表明了共享引用创建后必须给予有效初始化,可以使得代码更加安全简洁,保证了对象访问的安全性。无法主动释放共享引用,可以跟随对象释放减少引用计数器
  • 共享引用的安全性体现在,如果使用共享引用构建的对象,无法将对象空间设置为空。如果想释放内存,可以借助指向其他共享引用来减少引用计数,来释放空间
  • 共享引用本质,无法主动减少引用计数器,只能通过被动方法,例如生命周期终结,共享引用易主

基本语法操作

    // 定义
    TSharedRef<FTestClass> Ftc01;// 错误执行将导致崩溃
    TSharedRef<FTestClass> Ftc02(new FTestClass);// 正确
    // 解引用操作
    Ftc02->TestFun();
    (*Ftc02).TestFun();
    // 返回cosnt 引用 ,禁止将对象主动释放
    const FTestClass& Ftc03 = Ftc02.Get();
    // 和共享指针转换
    // 共享引用支持隐式转换为共享指针,由于共享引用是安全的,所以转换是隐式转换
    TSharedPtr<FTestClass> Ftc04 = Ftc02;
    // 从共享指针转换到共享引用是不安全的,所以需要调用TS函数
    TSharedRef<FTestClass> Ftc05 = Ftc04.ToSharedRef();

5、弱指针

  • 不会阻止对象的销毁,如果引用对象被销毁,则弱指针也将自动清空。一般弱指针的操作意图是保存了一个到达目标对象的指针,担不会控制该对象的生命周期,弱指针不会增加引用计数,可以用来断开引用循环问题。 、
  • 无论谁销毁了对象,只要其对象被销毁,弱指针都将自动清空,这使你能够安全缓存指向可变对象的指针。这也意味着,弱指针可能会意外清空,并且,你可以使用弱指针断开引用循环。
  • 当不再存在对对象的共享引用时,弱指针的对象将被销毁。
  • 弱指针有助于表明意图。当你在某个类中看到一个弱指针时,你就会明白该类仅缓存指向对象的指针,它并不控制它的生命周期。

基本语法操作

    // 定义
    // 构建空的弱指针
    TWeakPtr<FTestClass> WpFtc01;
    // 利用共享指针构建弱指针
    TSharedPtr<FTestClass> PFtc;
    TWeakPtr<FTestClass> WpFtc02(PFtc);
    // 利用共享引用构建弱指针
    TSharedRef<FTestClass> RFtc;
    TWeakPtr<FTestClass> WpFtc03(RFtc);
    // 检查是否有效
    // 检查弱指针指向的对象空间是否存在
    if (WpFtc01.IsValid()) {}
    // 释放操作
    // 主动释放,但是不会影响引用计数
    WpFtc01 = nullptr;

总结

  • 一块内存,如果存在有效引用(可直接到达内存的操作方式),则我们可以认为当前内存是有效并且合理的!但是当一块内存不存在引用,则我们可以视为此块内存为被弃用无效的,则可以回收重复利用,这就是内存垃圾回收机制的基本原理。
  • 智能指针强调的是当前内存的使用者存在多少,当不存在时,进行回收!
  • 注意:智能指针构建的均是栈对象数据类型

 

posted @ 2022-09-23 13:01  黎沐不吃香菜  阅读(1212)  评论(0编辑  收藏  举报