指针
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);//将指针在垃圾回收器中标记为强引用,无法被回收
}