美文网首页
21 UE5 指针:智能指针 和 STL指针

21 UE5 指针:智能指针 和 STL指针

作者: 游戏开发程序员 | 来源:发表于2024-04-06 23:19 被阅读0次

    STL 智能指针

    • auto_ptr、unique_ptr、shared_ptr 和 weak_ptr。
    • auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用

    unique_ptr

    • 不共享其指针,它不能复制,只能移动.
    • unique_ptr<Obj> p1 = make_unique<Obj>();
    • std::move(p1) 转移
    • p1.get();获取原始指针
    • release : 返回原指针,是否拥有权
    • reset: 换新删旧
    • swap: 交换管理对象

    shared_ptr

    • 为多个所有者管理对象在内存中的生命周期而设计
    • 可复制,可移动,通过引用计数技术
    • shared_ptr<Obj> sp(nullptr); 如果需要后面赋值的话
    • sp = make_shared<Obj>(); 构造
    • dynamic_pointer_cast、static_pointer_cast和const_pointer_cast来转换shared_ptr。
    • 类似于dynamic_cast、static_cast和const_cast运算符。

    shared_ptr 相关

    • 1 解决环式指向问题:weak_ptr
    • 2 获取自身对象的智能指针:shared_from_this()
    • 3 多组智能指针管理问题: 不可用1个raw pointer构造多个shared_ptr

    weak_ptr

    • 无拥有权,可复制,可移动

    • 常用于指向某一shared_ptr指针拥有的内存地址

    • 不会导致引用计数增加或减少。

    • Expired()判断非空后,使用Lock()来获取shared_ptr

    • STL智能指针代码演示

            // shared_ptr:记录数量是一种智能指针, 它能够记录多少个 shared_ptr 共同指向一个对象, 
            // 从而消除显示的调用 delete , 当引用计数变为零的时候就会将对象自动删除
            auto shared_pointer = make_shared<int>(10);
    
            // 获取原始的指针
            auto original_pointer = shared_pointer.get();
    
            // 查看对象的引用计数
            auto shared_pointer2 = shared_pointer;
            auto count = shared_pointer.use_count();
    
            // 释放 减少一个数量
            shared_pointer2.reset();
    
            // 全释放后 再查看原始指针数据已重置
            shared_pointer.reset();
    
            // unique_ptr 独占的智能指针, 它禁止其他智能指针与其共享同一个对象,从而保证代码的安全
            auto unique_pointer = make_unique<int>(20);
    
            // 非法获取 auto unique_pointer2 = unique_pointer;
    
            // 可使用move转移 转移后旧指针失效
            auto unique_pointer2 = move(unique_pointer);
    
            // 释放控制权,不释放内存
            auto p = unique_pointer2.release();
            *p = 21;
    
            // 释放内存
            unique_pointer2.reset();
    
            // weak_ptr 配合shared_ptr而引入,解决shared_ptr的相互引用
            // 不拥有资源的所有权,不能直接使用。无 * 和 ->
            // 通过lock获取,和expired检测是否过期。
            auto shared_p = make_shared<string>("This is shared_ptr!");
    
            // 不增加计数
            weak_ptr<string> weak_p = shared_p;
    
            // 没有过期,输出内容
            if (!weak_p.expired())
            {
                // lock()获取智能指针
                auto str = *weak_p.lock();
                *weak_p.lock() = "Change by weak_p";
                str = *weak_p.lock();
            }
    
            // 清空
            weak_p.reset();
    

    UE指针关系

    • TSharedPtr<---->std::shared_ptr
    • TSharedFromThis<---->std::enable_shared_from_this
    • TWeakPtr<---->std::weak_pt
    • TUniquePtr<---->std::unique_ptr

    TSharedPtr

    • 用于UE中非UObject相关的类
    • 非侵入式,性能佳,
    • 优先选择共享引用(不可为空)。

    TSharedRef

    • 构造:TSharedPtr<int> sp = MakeShared<int>(666);
    • 构建必须初始化 不可为空
    • 没有空值需求下,优先使用ref而不是ptr
    • 无Reset() 和 IsValid() 接口
    • 共享 +1: TSharedRef<int> sr = sp.ToSharedRef();

    TWeakPtr

    • 大多数用于获取SharedPtr
    • 只引用 不计数
    • 通过Pin()获取指向的SharedPtr
    • 源SharedPtr失效,他也失效
            // TSharedPtr
            TSharedPtr<int32> MyIntSharedPtr(new int32(123));
            check(MyIntSharedPtr.IsValid()); // 有效
            check(MyIntSharedPtr.IsUnique()); // 唯一
    
            TSharedPtr<int32> MyIntSharedPtr2; // 可创建为空
            MyIntSharedPtr2 = MyIntSharedPtr; // 复制
            check(MyIntSharedPtr.GetSharedReferenceCount() == 2);
    
            const int32 DeferenceTest = *MyIntSharedPtr;
            MyIntSharedPtr.Reset(); // 重置
            check(MyIntSharedPtr.GetSharedReferenceCount() == 0);
    
            // checkf 调试模式下有效: 断言失败会中断程序,打印日志
            // checkf(MyIntSharedPtr.IsValid(), TEXT("MyIntSharedPtr is not Valid "));
            
            // TWeakPtr
            TSharedPtr<int32> SharedInt(new int32(64));
            TWeakPtr< int32 > WeakInt(SharedInt);
            // Pin() 返回 TSharedPtr
            check(WeakInt.Pin().IsValid());
            WeakInt.Reset(); // 重置后,就失效了
            check(!WeakInt.Pin().IsValid());
    
            // 绑定的智能指针重置了,也会失效
            TWeakPtr< int32 > WeakInt2(SharedInt);
            SharedInt.Reset();
            check(!WeakInt2.Pin().IsValid());
    
            // TSharedRef
            // 构造必须初始化 优先使用TSharedRef而不是TSharedPtr
            TSharedRef< float > FloatRef(new float(123.0f));
            const float& MyFloat = *FloatRef; // 获取内容的引用1
            const float& MyFloat2 = FloatRef.Get(); // 获取内容的引用2
    
            TSharedRef< float > FloatRef2 = FloatRef; // 赋值  保留了123
    
            FloatRef = MakeShareable(new float(456.0f));
    
            // 解决类内部获取自身:继承TSharedFromThis, 调用AsShared()
            // 并且声明函数标签 nodiscard : 函数的返回值应该被检查并使用.
    

    TUniquePtr

    • 对内存所有权的唯一性
    • 不能有赋值 和 拷贝操作
    • 只有MoveTemp() 转移内存所有权(类似:std::move())
    • 可用MakeUnique构造
            // TUniquePtr 带参构造函数被关键字 explicit 标记 
            TUniquePtr<int> up1(new int(1));
            // 强烈建议用MakeUnique接口 
            TUniquePtr<int> up2(MakeUnique<int>(2));
            TUniquePtr<int> up3 = MakeUnique<int>(3);
    
            // 拷贝/赋值重载被关键字 =delete 标记 
            // TUniquePtr<int> up4 = up1; // 编译报错 
            // TUniquePtr<int> up5(up2); // 编译报错 
    
            // 只能通过 MoveTemp() 转移内存所有权, 
            // 转移后up1 被析构 
            TUniquePtr<int> up6 = MoveTemp(up1);
    

    TSharedFromeThis

    • 类公有继承自TSharedFromTthis<ClassName>
    • 提供两类接口 AsShared() 和 SharedThis()
    • 调用AsShared() 的对象必须是一个智能指针
    • 不能在构造函数内部使用
    • 解决类内部获取指针的问题(会导致被析构多次)
            class MyClass : public TSharedFromThis<MyClass>
            {
            public:
                TSharedRef<MyClass> SharedMyself()
                {
                    return SharedThis(this);
                }
    
                ~MyClass() 
                {
                    UE_LOG(TestLog, Warning, TEXT("MyClass deleted"));
                }
            };
    
            // 普通指针或对象,使用TSharedFromThis内的方法会触发断言 
            TSharedPtr<MyClass> ptr = MakeShared<MyClass>();
    
            // 通过接口获取类实例的智能引用,维护的是同一块内存,同一个计数器 
            TSharedRef<MyClass> pRef1 = ptr->AsShared();
            TSharedRef<MyClass> pRef2 = ptr->SharedMyself();
    
    • 以上代码会正确的析构, 以为是一组智能指针.
    • 用直接返回this, 就会导致多组智能指针 ptr(new MyClass)

    MakeShared

    • 比直接用普通指针创建效率更高
    • 包括用于TUniquePtr的MakeUnique
    • MakedShared只需要进行一次内存申请(2块内存 1数据1控制块)

    MakeShareable

    • 将一个普通指针转换为智能指针
    • MakeShareable 支持自定义删除对象的行为:deleter

    为什么不能用普通指针初始化智能指针

    • 普通指针不具备自动管理内存的功能,需要手动调用delete来释放内存。
    • 如果将普通指针直接赋值给智能指针,会导致同一个内存块被智能指针和普通指针同时管理,从而可能导致悬空指针、内存重复释放等严重问题。

    TObjectPtr介绍

    • 用于管理对 UObject(Unreal Object)对象的指针。
    • 提供了一些方便的功能,例如自动释放、空指针检查和安全的操作。
    • 自动释放:当 TObjectPtr 对象超出作用域或被销毁时,会调用 Reset() 释放 UObject 对象。
    • 安全性检查: TObjectPtr 提供了成员函数 IsValid() 和 IsValidPtr()
    • 操作符重载: TObjectPtr 重载了箭头操作符 -> 和解引用操作符 *
    • 使用场景: 需要对 UObject 进行动态管理和安全访问的场景中特别有用
    • 内部是一个FObject Handle(本质就是一个指针)

    相关文章

      网友评论

          本文标题:21 UE5 指针:智能指针 和 STL指针

          本文链接:https://www.haomeiwen.com/subject/kughzdtx.html