UE4 智能指针和弱指针

一、序言

       我们知道C++里面有自己的指针,但是在虚幻引擎中原生C++的指针在分配和释放内存时会产生一些问题,比如无法被虚幻进行回收,造成内存泄漏等问题,于是虚幻就衍生出了智能指针。虚幻智能指针库是 C++11 智能指针的自定义实现,旨在减轻内存分配和跟踪的负担。此实现包括行业标准共享指针弱指针唯一指针。它还添加了类似于不可为空的共享指针的共享引用这些类不能与UObject系统一起使用,因为虚幻对象使用单独的内存跟踪系统,该系统针对游戏代码进行了更好的调整。

二、各个指针的含义

       共享指针(TSharedPrt):共享指针拥有它引用的对象,无限期地阻止删除该对象,并最终在没有共享指针或共享引用,引用它时处理它的删除。共享指针可以为空,这意味着它不引用任何对象。任何非空共享指针都可以生成对其引用的对象的共享引用。

       共享引用(TSharedRef):共享引用的作用类似于共享指针,因为它拥有它所引用的对象。它们在空对象方面有所不同;共享引用必须始终引用非空对象。因为共享指针没有这个限制,所以共享引用总是可以转换为共享指针,并且共享指针保证引用一个有效的对象。当您想要保证被引用的对象是非空的,或者如果您想要指示共享对象的所有权时,请使用共享引用。

       弱指针(TWeakPtr):弱指针类似于共享指针,但不拥有它们引用的对象,因此不会影响其生命周期。这个属性非常有用,因为它打破了引用循环,但这也意味着弱指针可以随时变为空,而不会发出警告。出于这个原因,弱指针可以生成指向它引用的对象的共享指针,从而确保程序员临时安全地访问该对象。

       唯一指针(TUniquePtr):唯一指针单独且明确地拥有它所引用的对象。由于给定资源只能有一个唯一指针,因此唯一指针可以转移所有权,但不能共享它。任何复制唯一指针的尝试都将导致编译错误。当唯一指针超出范围时,它会自动删除它引用的对象。
 

三、指针的用法

         为了更好的使用指针,虚幻提供了几个帮助类和函数,使使用智能指针更容易、更直观。

TSharedFromThis

TSharedFromThis添加AsSharedSharedThis函数派生您的类。这些函数使您能够获取TSharedRef到您的对象。
 

MakeSharedMakeShareable

从常规 C++ 指针创建共享指针。MakeShared在单个内存块中分配新的对象实例和引用控制器,但要求对象提供公共构造函数。MakeShareable效率较低,但即使对象的构造函数是私有的也可以工作,使您能够获得不是您创建的对象的所有权,并在删除对象时支持自定义行为。

StaticCastSharedRefStaticCastSharedPtr

静态转换实用函数,通常用于向下转换为派生类型。

ConstCastSharedRefConstCastSharedPtr

const智能参考或智能mutable指针分别转换为智能参考或智能指针。

四、使用智能指针的优缺点

优点:

  • std::Shared_Ptr不是在所有的平台上都可用。

  • 使得在所有的编译器和平台上更加一致性。

  • 可以和其他虚幻容器无缝协作。

  • 更好的控制平台特性,包括线程处理和优化。

  • 线程安全的功能。

  • 我们想在性能方面有更多的控制权(内敛函数,内存,虚函数等)。

  • 在不需要的时候不需要引入第三方依赖。

缺点:

  • 创建和复制智能指针比创建和复制原始 C++ 指针涉及更多开销。

  • 维护引用计数会增加基本操作的周期。

  • 一些智能指针比原始 C++ 指针使用更多内存。

  • 参考控制器有两个堆分配。使用MakeShared而不是MakeShareable避免第二次分配,并且可以提高性能。

五、案例测试

   

  • 原生C++与智能指针之间的转化

创建一个C++ Actor类 命名为 MyActor
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
class TaskA
{
public:
	int32 a;
	float b;
};//创建一个原生的C++类

UCLASS()
class HEXINU4_API AMyActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AMyActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	void TaskAA();//共享指针测试函数
       void TaskSharedRef();//共享引用测试函数
       void TaskSharedRefAndPtr();//共享指针和共享引用之间相互转化
       void TaskweakPrt();//弱指针的测试函数
        
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	TSharedPtr<TaskA> Task_a;//共享指针可以为 NULL 但是不可以复制,使用共享指针最好全局指针,不要创建临时指针好它比普通的指针耗资源
};
        // TSharedRef<TaskA> Task_b;//不能这么声明共享引用是错误的
           TWeakPrt<TaskA> Task_c;//弱指针的声明
void AMyActor::TaskAA()
{
	Task_a= MakeShareable(new TaskA());//共享指针创建一个智能指针
	if (Task_a.IsValid() || Task_a.Get())//判断共享指针是否有效或者解引用
	{
		Task_a.Get()->a;//可替换为Task_a->a;
		Task_a.Reset();
	}
}
void AMyActor::TaskSharedRef()
{
      TSharedRef<TaskA> Task_b(new TaskA());//共享引用必须初始化可用对象
       Task_b->a;
}
void  AMyActor::TaskSharedRefAndPtr()
{
      //共享引用转化成共享指针
       TSharedRef<TaskA> Task_b(new TaskA());
       Task_a = Task_b;
      //普通指针转化成共享指针
       TaskA *NewTaskA = new TaskA();
       Task_a = MakeShareable(new NewTaskA); 
      //注意
     // Task_b = nullptr;//错误的
        Task_b = Task_a.ToSharedRef();//共享指针转换成共享引用是不安全的它有个断言
}
 void AMyActor::TaskweakPrt()
{
      TSharedPtr<TaskA> _TaskA_ptr = MakeShareable(new TaskA());//共享指针
      TSharedRef<TaskA> _TaskA_ref(new TaskA());//共享引用
      TweakPtr<TaskA>Task_D(_TaskA_ptr);
      TweakPtr<TaskA>Task_K(_TaskA_ref);
      Task_c = Task_D;
      Task_c = Task_K;
        Task_c = nullptr;//防止对象被销毁
      //弱指针转化成智能指针
      TSharedPtr<TaskA> NewTask(Task_c.Pin());
       if(NewTask.IsValid())
         {
             NewTask->a;
         }

}
  • 智能指针之间的转化

class FBase
{
public:
protected:
private:
};

class FB : public FBase
{
public:
	void Printf() {};
protected:
private:
};
	
//将基类转化成派生类,访问派生类的函数和变量
	TSharedPtr<FBase> B_ = MakeShareable(new FB());//共享指针和UObject不兼容,UObject有自己的销毁机制
	TSharedPtr<FB> C_ = StaticCastSharedPtr<FB>(B_);
	if (C_.IsValid())
	{
		C_->Printf();
	}
	const TSharedPtr<FBase> E_ = MakeShareable(new FB());
	TSharedPtr<FBase> D_ = ConstCastSharedPtr<FBase>(E_);
	TSharedPtr<FB> F_ = StaticCastSharedPtr<FB>(D_);
	if (F_.IsValid())
	{
		F_->Printf();
	}
  • TSharedFromThis()的用法:保留一个对对对象的弱引用,方便直接转化成共享引用AsShared();

class TaskA:public TSharedFromThis<TaskA>
{
public:
	int32 a;
    float b;
};
.CPP文件中测试
       TSharedPtr<TaskA>NewTest = MakeShareable(new TaskA());
	newtask->a;
	TaskA *NewTest1 = NewTest.Get();//解引用将智能指针转化成原生指针
	if (NewTest1)
	{
		NewTest1->AsShared();
	}
	TaskA *currentTask = new TaskA();//原生指针
	TSharedRef<TaskA> Task_c = currentTask->AsShared();
  • 综合案例

#include "CoreMinimal.h"//

class IMyID//一个用户ID类
{
public:
	IMyID()
	{
		ID = FMath::RandRange(100, 1000);
	}
	FORCEINLINE uint64 GetID() { return ID; }//强制内敛ID
private:
	int32 ID;
};

class FData:public IMyID//数据继承ID类,有自己的数据参数
{  
public:
	FData()
	{
		Health = 100.f;
		bDeath = 0;
		PlayerName = TEXT("CharacterOne");
	}

	float Health;
	uint8 bDeath;
	FName PlayerName;
};

class FDataManager//数据管理类
{
public:
	static TSharedRef<FDataManager> Get()
	{
		if (DataMager.IsValid())
		{
			DataMager = MakeShareable(new FDataManager());
		}
		return DataMager.ToSharedRef();
	}                           
	TSharedRef<FData> CreatData()
	{
		TSharedPtr<FData> tmp_Data = MakeShareable(new FData());
		MyData.Add(tmp_Data->GetID(), tmp_Data);
		return tmp_Data.ToSharedRef();
	}
	~FDataManager()
	{
	}
private:
	static TSharedPtr<FDataManager> DataMager;
	TMap<uint64, TSharedPtr<FData>> MyData;
};
//角色实例显示
class FCharacter
{
public:
	FORCEINLINE bool IsValid()
	{ 
		return NewData.IsValid();
	}
	void SetNewData(TSharedRef<FData> CurrentNewData) { NewData = CurrentNewData; }
	void SetNewName( FString NewName)
	{
		if (NewData.IsValid())
		{
			NewData.Pin()->PlayerName = FName(*NewName);
		}
	}
	FORCEINLINE bool IsValid() { return NewData.IsValid(); } 
	
protected:
private:         
	TWeakPtr<FData> NewData;//弱指针不参与类的生命周期

};
void NewMain()//假设是一个场景
{
	FCharacter* Character = new FCharacter();

	Character->SetNewData(FDataManager::Get()->CreatData());
	Character->SetNewName("PlayerTwo");
}
//注意如果是一个裸指针(C++原生指针)当删除这个指针时会崩溃,产生野指针,解决方法是加上一个TSharedFromThis(FData),让它始终被一个弱指针包裹
class FData:public IMyID,public TSharedFromThis(FData)//数据继承ID类,有自己的数据参数
{  
public:
	FData()
	{
		Health = 100.f;
		bDeath = 0;
		PlayerName = TEXT("CharacterOne");
	}

	float Health;
	uint8 bDeath;
	FName PlayerName;
};
	FData* CreatData()
	{
		TSharedPtr<FData> tmp_Data = MakeShareable(new FData());
		MyData.Add(tmp_Data->GetID(), tmp_Data);
		return tmp_Data.Get();
	}

void NewMain()//假设是一个场景
{
	FCharacter* Character = new FCharacter();
          FData* New_Data = FDataManager::Get()->CreatData();
	Character->SetNewData(New_Data->AsShared());
	Character->SetNewName("PlayerTwo");
}

猜你喜欢

转载自blog.csdn.net/qq_43021038/article/details/125987920