基本概念

  • UE4 对 UObject 对象提供垃圾回收
  • UE4 对原生对象不提供垃圾回收,需要手动进行清理
    • 方式

      • malloc / free
      • new / delete

        new与malloc的区别在于,new在分配内存完成之后会调用构造函数。
    • 缺点
      • 如果不及时清理,则会占用内存,或者导致内存泄漏
      • 如果不小心提前清理,则会导致野指针
  • UE4 提供共享指针库来管理内存,它是C++11智能指针的自定义实现
    • 分类

      • TSharedPtr
      • UniquePtr
      • TWeakPtr
      • TSharedRef
    • 优点
      • 防止内存泄漏 共享引用不存在时,智能指针(弱指针除外)会自动删除对象。
      • 弱引用 弱指针会中断引用循环并阻止悬挂指针。
      • 可选择的线程安全 虚幻智能指针库包括线程安全代码,可跨线程管理引用计数。如无需线程安全,可用其换取更好性能。
      • 运行时安全 共享引用从不为空,可固定随时取消引用。
      • 授予意图 可轻松区分对象所有者和观察者。
      • 内存 智能指针在64位下仅为C++指针大小的两倍(加上共享的16字节引用控制器)。唯一指针除外,其与C++指针大小相同。

共享指针 TSharedPtr

  • TSharedPtr 不能指向 UObject。如果想要指向UObject,可以使用TWeakObjectPtr
  • TSharedPtr 可以对FStructures 使用

创建/初始化/ 重置

  • MakeShareable()/MakeShared<T>() 函数

  • Reset() 函数

    class SimpleObject {
    public:
    SimpleObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"SimpleObject Construct")); }
    ~SimpleObject() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"SimpleObject Destruct")); }
    void ExeFun() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"Execute")); }
    };
    // 快速创建共享指针
    TSharedPtr<SimpleObject> simObjectPtr(new SimpleObject());
    // MakeShareable 创建共享指针
    TSharedPtr<SimpleObject> simObjectPtr2 = MakeShareable(new SimpleObject());
    // 创建线程安全
    TSharedPtr<SimpleObject, ESPMode::ThreadSafe> simObjectPtr3 = MakeShareable(new SimpleObject());
    // 查看引用计数 UE_LOG(LogTemp, Warning,
    TEXT(__FUNCTION__"引用计数: simObjectPtr[%d], simObjectPtr2[%d], simObjectPtr3[%d] "),
    simObjectPtr.GetSharedReferenceCount(), simObjectPtr2.GetSharedReferenceCount(), simObjectPtr3.GetSharedReferenceCount()); // 重置共享指针
    simObjectPtr.Reset();
    simObjectPtr2 = nullptr;

复制/转移

  • 赋值

  • MoveTemp / MoveTempIfPossible

    // 复制共享指针
    TSharedPtr<SimpleObject> simObjectPtr_copy = simObjectPtr;
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"引用计数: simObjectPtr[%d], simObjectPtr_copy[%d],"),
    simObjectPtr.GetSharedReferenceCount(), simObjectPtr_copy.GetSharedReferenceCount()); // 转移共享指针
    TSharedPtr<SimpleObject> simObjectPtr_MoveTemp = MoveTemp(simObjectPtr_copy); // 另 MoveTempIfPossible()
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"引用计数: simObjectPtr[%d], simObjectPtr_copy[%d], simObjectPtr_MoveTemp[%d]"),
    simObjectPtr.GetSharedReferenceCount(), simObjectPtr_copy.GetSharedReferenceCount(), simObjectPtr_MoveTemp.GetSharedReferenceCount()

条件判断 / 对比 / 解引用与访问

  • -> 运算符

  • Get() 函数

  • IsValid() 函数

  • == != 运算符

    if (simObjectPtr)					// 条件判断
    {
    simObjectPtr->ExeFun(); // 解引用
    }
    if (simObjectPtr.Get() != nullptr) // 条件判断
    {
    simObjectPtr.Get()->ExeFun(); //解引用
    }
    if (simObjectPtr.IsValid()) // 条件判断
    {
    (*simObjectPtr).ExeFun(); // 解引用
    }
    if (simObjectPtr == simObjectPtr_copy) // 对比
    {
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"simObjectPtr_copy == simObjectPtr"));
    }


共享引用 TSharedRef

  • 共享引用不可为空
  • 不可用于 UObject对象
  • 没有 IsValid() 函数

创建/初始化

  • MakeShareable()/MakeShared<T>() 函数

    // 创建共享引用
    TSharedRef<SimpleObject> objRef(new SimpleObject());
    TSharedRef<SimpleObject> objRef2 = MakeShareable(new SimpleObject());
    TSharedRef<SimpleObject> objRef3 = MakeShared<SimpleObject>();

TSharedRef 与 TSharedPtr转换

  • 隐式转化

  • ToSharedRef()

    // TSharedRef -> TSharedPtr
    TSharedPtr<SimpleObject> objPtr = objRef; // TSharedPtr -> TSharedRef
    objRef3= objPtr.ToSharedRef();

比较

  • 没有 IsValid() 函数

    // 共享指针比较
    if (objRef == objRef3)
    {
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"objRef == objRef3 , 引用计数:%d"), objPtr.GetSharedReferenceCount());
    }


弱指针 TWeakPtr

  • 与TSharedPtr相比,不参与引用计数
  • 对象不存在共享指针时,TWeakPtr将自动失效
  • 使用时需要判断有效性

创建/初始化/转换/重置

  • 通过 TSharedPtr 创建

  • 通过 TSharedRef 创建

  • 运算符 = 赋值

  • IsValid() 函数判断有效性

  • Pin() 函数转成 TSharedPtr ,再解引用访问对象

  • Reset()nullptr 重置

    // 强指针创建弱指针
    TSharedPtr<SimpleObject> ObjPtr=MakeShared<SimpleObject>();
    TWeakPtr<SimpleObject> ObjWeakPtr(ObjPtr);
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"step1 引用计数:ObjPtr[%d]"), ObjPtr.GetSharedReferenceCount()); //强引用创建弱指针
    TSharedRef<SimpleObject> objRef = MakeShareable(new SimpleObject());
    TWeakPtr<SimpleObject> ObjWeakPtr2(objRef); TWeakPtr<SimpleObject> ObjWeakPtr_Copy = ObjWeakPtr;
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"step2 引用计数:ObjPtr[%d]"), ObjPtr.GetSharedReferenceCount()); // 判断有效性
    if (ObjWeakPtr.IsValid())
    {
    TSharedPtr<SimpleObject> ObjPtr2 = ObjWeakPtr.Pin();
    ObjPtr2->ExeFun();
    } // 清空强指针
    ObjPtr.Reset();
    TSharedPtr<SimpleObject> ObjPtr2 = ObjWeakPtr.Pin();
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"step3 引用计数:ObjPtr[%d]"), ObjPtr.GetSharedReferenceCount()); // 判断有效性
    if (!ObjPtr2)
    {
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"弱指针已空 "));
    } // 重置
    ObjWeakPtr.Reset();
    ObjWeakPtr_Copy = nullptr;


唯一指针 TUniquePtr

  • TUniquePtr 指向的对象只能被唯一指向,因而 Unique指针不能赋值给其它指针
  • 不要为共享指针或共享引用引用的对象创建唯一指针

创建/初始化/判断/解引用/重置

  • MakeUnique()

  • IsValid()

  • -> 运算符

  • Get() 函数

  • Release() 释放并返回指针

  • Reset()nullptr 重置

    // 创建唯一指针
    TUniquePtr<SimpleObject> ObjUniquePtr = MakeUnique<SimpleObject>();
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Validity: ObjUniquePtr[%d]"), ObjUniquePtr.IsValid()); // 判断有效性
    if (ObjUniquePtr.IsValid())
    {
    ObjUniquePtr->ExeFun(); // 解引用
    } // 释放指针,移交
    TUniquePtr<SimpleObject> ObjUniquePtr2(ObjUniquePtr.Release());
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Validity: ObjUniquePtr[%d], ObjUniquePtr2[%d]"), ObjUniquePtr.IsValid(), ObjUniquePtr2.IsValid()); // 重置
    ObjUniquePtr.Reset();
    ObjUniquePtr2 = nullptr;


基类与派生类的智能转换

共享指针转换

  • 派生类转基类 隐式转换

  • 基类转派生类 StaticCastSharedPtr

  • 非常量转常量 ConstCastSharedPtr

    TSharedPtr<SimpleObject> simpleObj;
    TSharedPtr<ComplexObject> complexObj = MakeShared<ComplexObject>(); // 派生类转基类
    simpleObj = complexObj;
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"simpleObj is %s"), simpleObj.IsValid() ? TEXT("Valid") : TEXT("Not Valid")); // 基类转派生类
    TSharedPtr<ComplexObject> complexObj2 = StaticCastSharedPtr<ComplexObject>(simpleObj);
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"complexObj2 is %s"), complexObj2.IsValid() ? TEXT("Valid") : TEXT("Not Valid")); // 常量指针转非常量指针 const TSharedPtr<SimpleObject> simpleObj_const(new SimpleObject());
    TSharedPtr<SimpleObject> simpleObj_mutable = ConstCastSharedPtr<SimpleObject>(simpleObj_const);
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"simpleObj_mutable is %s"), simpleObj_mutable.IsValid() ? TEXT("Valid") : TEXT("Not Valid"));

共享引用转换

  • 隐式转换
  • StaticCastSharedRef

// 创建唯一指针

TUniquePtr ObjUniquePtr = MakeUnique();

UE_LOG(LogTemp, Warning, TEXT(FUNCTION" Validity: ObjUniquePtr[%d]"), ObjUniquePtr.IsValid());

// 判断有效性
if (ObjUniquePtr.IsValid())
{
ObjUniquePtr->ExeFun(); // 解引用
} // 释放指针,移交
TUniquePtr<SimpleObject> ObjUniquePtr2(ObjUniquePtr.Release());
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Validity: ObjUniquePtr[%d], ObjUniquePtr2[%d]"), ObjUniquePtr.IsValid(), ObjUniquePtr2.IsValid()); // 重置
ObjUniquePtr.Reset();
ObjUniquePtr2 = nullptr;
  • ConstStaticCastSharedRef

代码省略


助手类 TSharedFromThis

  • 自定义类继承 TSharedFromThis 模板类

  • TSharedFromThis 会保存一个弱指针

  • AsShared() 将裸指针转智共享引用,可再隐式转为共享指针

  • SharedThis(this) 会返回具备"this"类型的TSharedRef

  • 不要在构造函数中调用 AsShared 或 Shared,共享引用此时并未初始化,将导致崩溃或断言

    // 基类
    class BaseClass :public TSharedFromThis<BaseClass>
    {
    public:
    BaseClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
    virtual ~BaseClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
    virtual void ExeFun() {
    TSharedRef<BaseClass> ThisAsSharedRef = AsShared();
    }
    }; // 派生类
    class ChildClass :public BaseClass
    {
    public:
    ChildClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
    virtual ~ChildClass() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__)); }
    virtual void ExeFun() override{
    //AsShared()返回 TSharedRef<BaseClass>, 因而编译不通过
    //TSharedRef<ChildClass> AsSharedRef = AsShared(); TSharedRef<ChildClass> AsSharedRef = SharedThis(this);
    }
    };
    TSharedPtr<BaseClass> BaseClassPtr = MakeShared<BaseClass>();
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 引用计数:BaseClassPtr[%d]"), BaseClassPtr.GetSharedReferenceCount()); BaseClass* tempPtr = BaseClassPtr.Get();
    TSharedPtr<BaseClass> BaseClassPtr_Shared =tempPtr->AsShared();
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 引用计数:BaseClassPtr[%d], BaseClassPtr_Shared[%d]"),
    BaseClassPtr.GetSharedReferenceCount(), BaseClassPtr_Shared.GetSharedReferenceCount()); // 使用下面语句运行,程序死机
    // TSharedPtr<BaseClass> BaseClassPtr_New = MakeShareable(tempPtr);


注意

  • 避免将数据作为 TSharedRef 或 TSharedPtr 参数传到函数,此操作将因取消引用和引用计数而产生开销。相反,建议将引用对象作为 const & 进行传递。
  • 共享指针与虚幻对象(UObject 及其衍生类)不兼容。引擎具有 UObject 管理的单独内存管理系统(对象处理文档),两个系统未互相重叠。

参考

【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef的更多相关文章

  1. 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载

    同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...

  2. 【UE4 C++ 基础知识】<4> 枚举 Enum、结构体 Struct

    枚举 UENUM宏搭配BlueprintType可以将枚举暴露给蓝图,不使用的话,仅能在C++使用 //定义一个原生enum class enum class EMyType { Type1, Typ ...

  3. 【UE4 C++ 基础知识】<12> 多线程——FRunnable

    概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...

  4. 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换

    基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...

  5. 【UE4 C++ 基础知识】<10>资源的引用

    2种引用方式 硬引用(Hard Reference) 即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载 硬引用过多会导致运行时很多暂时用不到的资源也被加载到内存中 大量资源会导致进程阻 ...

  6. 【UE4 C++ 基础知识】<5> 容器——TArray

    概述 TArray 是UE4中最常用的容器类.其速度快.内存消耗小.安全性高. 其设计时未考虑扩展问题,因此建议在实际操作中勿使用 新建(new) 和 删除(delete) 创建或销毁 TArray ...

  7. 【UE4 C++ 基础知识】<8> Delegate 委托

    概念 定义 UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系. 监听者通过将响应函数绑定到委托上,使得委托触发时立即收到 ...

  8. 【UE4 C++ 基础知识】<6> 容器——TMap

    概述 TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中. 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取 映 ...

  9. 【UE4 C++ 基础知识】<13> 多线程——TaskGraph

    概述 TaskGraph 系统是UE4一套抽象的异步任务处理系统 TaskGraph 可以看作一种"基于任务的并行编程"设计思想下的实现 通过TaskGraph ,可以创建任意多线 ...

随机推荐

  1. 🏆【Alibaba工具型技术系列】「EasyExcel技术专题」摒除OOM!让你的Excel操作变得更加优雅和安全

    前提概要 针对于后端开发者而言的,作为报表的导入和导出是一个很基础且有很棘手的问题!之前常用的工具和方案大概有这么几种: JXL(Java Excel API 工具服务),此种只支持xls的文件格式, ...

  2. VMware安装IPFire防火墙镜像

    之后便可以通过WEB登录到管理页面(admin账号,密码是在上面配置的) 详细可参考:https://www.mobibrw.com/2016/4900

  3. GC-优化-案例

    优化案例 Service S 优化 现在看一下执行jstat -gcutil的结果 S0 S1 E O P YGC YGCT FGC FGCT GCT 12.16 0.00 5.18 63.78 20 ...

  4. python中dump与dumps的区别

    刚写了一个代吗,没有搞懂dump和dumps的区别,现在搞懂了,下班后在来整理import pickleq = [1,2,3,4]pickle.dump(q,open("cb1.txt&qu ...

  5. logstash-input-jdbc配置说明

    Logstash由三个组件构造成,分别是input.filter以及output.我们可以吧Logstash三个组件的工作流理解为:input收集数据,filter处理数据,output输出数据.至于 ...

  6. 自己实现一个Controller——精简型

    写在最前 controller-manager作为K8S master的其中一个组件,负责众多controller的启动和终止,这些controller负责监控着k8s中各种资源,执行调谐,使他们的实 ...

  7. html2canvas实现截取指定区域或iframe的区域

    官网文档: http://html2canvas.hertzen.com/ 使用的是 jquery 3.2.1   html2canvas 1.0.0-rc.7 截取根据id的指定区域: var ca ...

  8. 一朵云、一张网、一体化 ——GRTN 打造最佳流媒体场景实践

    阿里巴巴 GRTN 是面向流媒体云原生设计的,方便客户构建自己的流媒体云原生应用,让流媒体服务无处不在. 在近期召开的分布式云主题报告会上,阿里云资深技术专家卢日发表了题为<GRTN 打造阿里云 ...

  9. javascript 一些函数的实现 Function.prototype.bind, Array.prototype.map

    * Function.prototype.bind Function.prototype.bind = function() { var self = this, context = [].shift ...

  10. MyBatis Plus 批量数据插入功能,yyds!

    最近 Review 小伙伴代码的时候,发现了一个小小的问题,小伙伴竟然在 for 循环中进行了 insert (插入)数据库的操作,这就会导致每次循环时都会进行连接.插入.断开连接的操作,从而导致一定 ...