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(本质就是一个指针)
网友评论