moushen

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

初识共享指针与共享引用

UE4对于不继承UObject的类提供了一系列转换模板类与接口方法,十分方便,能够为自己的原生类进行垃圾回收,不用太担心野指针等恶性问题。

对于原生类,需要继承TSharedFromThis<T>来实现AsShared()方法。

class TaskA:public TSharedFromThis<TaskA>
{
public:
    int32 a=155;
    float b;
    //单例,也是TSharedFromThis派生类的运用
    static TSharedRef<TaskA> Get() {
        if (m_Task_a.IsValid() || m_Task_a.Get())
        {
            //new一个类,然后将该类转换为共享指针并赋值给静态变量
            m_Task_a = MakeShareable(new TaskA());
        }
        //转换为引用并返回
        return m_Task_a->AsShared();
    }
private:
    static TSharedPtr<TaskA> m_Task_a;
};

 

void ATaskActor::TestAA()
{
    Task_a = MakeShareable(new TaskA());
    if (Task_a.IsValid() || Task_a.Get())
    {
        int32 temp = Task_a->a;
        GEngine->AddOnScreenDebugMessage(-1, 50.0f, FColor::Red, FString::FromInt(temp));
        Task_a.Reset();
    }
}

void ATaskActor::TaskShareRef()
{
    //共享引用
    TSharedRef<TaskA> Task_b(new TaskA());
    Task_b->a;
    (*Task_b).a;
}

TSharedPtr<TaskA> TaskA::m_Task_a;

void ATaskActor::TaskRefAndPtr()
{
    //将共享引用转换为共享指针
    TSharedRef<TaskA> Task_c(new TaskA());
    Task_a = Task_c;
    //将普通指针转换为共享指针
    TaskA* tempa = new TaskA();
    Task_a = MakeShareable(tempa);
    //将共享指针转换为共享引用
    if (Task_a.IsValid() && Task_a.Get())
    {
        Task_c = Task_a.ToSharedRef();
    }
    //将普通指针变成共享引用,在类继承了TSharedFromThis后,就可以通过AsShared()获得引用。
    TaskA* currentTaskA = new TaskA();
    TSharedRef<TaskA> CurrentRefTaskA = currentTaskA->AsShared();
    CurrentRefTaskA->a;
    //声明完类里的私有变量后,才能使单例运作
    TaskA::Get()->a;
}

弱指针

一般共享指针,在引用为0或者置空后,会被销毁,但是弱指针不会,对象依旧存在

class FTreeNode 
{
    //树结构,反正就是利用弱指针置空后不会销毁的机制
    TArray<TSharedPtr<FTreeNode>> Children_;

    TWeakPtr<FTreeNode> Parent_;
};

 

void ATaskActor::TaskWeakPtr()
{
   TWeakPtr<TaskA> Task_E;
TSharedPtr
<TaskA> _TaskA_ptr = MakeShareable(new TaskA()); TSharedRef<TaskA> _TaskA_Ref(new Task());
//创建 TWeakPtr<TaskA> Task_D(_TaskA_ptr); TWeakPtr<TaskA> Task_K(_TaskA_Ref);
//转换,共享指针->弱指针 Task_E = Task_D; Task_E = Task_K;
//使用完弱指针后重置 Task_E = nullptr;
//弱指针->共享指针 TSharedPtr<TaskA> NewTask(Task_E.Pin()); if (NewTask.Get()) { NewTask->a; } }

 

智能指针的优缺点

 

 

 强引用与弱引用

 在智能指针的项目应用中,主要是可以让弱引用和强引用通信,并使强引用不会变成野指针
 1 class IMyID {
 2     IMyID() {
 3         _id = FMath::RandRange(100, 10000);
 4     }
 5     FORCEINLINE uint64 GetID() { return _id; }
 6 private:
 7     uint64 _id;
 8 };
 9 //数据
10 class FData :public IMyID ,TSharedFromThis<FData>
11 {
12 public:
13     FData()
14     {
15         _health = 100.0f;
16         bDeath = 0;
17         PlayerName = TEXT("yuyang");
18     };
19 public:
20     float _health;
21     uint8 bDeath : 1;
22     FName PlayerName;
23 };
24 
25 //数据管理
26 class FDataManager {
27 public:
    //单例
28 static TSharedRef<FDataManager> Get() 29 { 30 if (!_DataManager.IsValid()) 31 { 32 _DataManager = MakeShareable(new FDataManager()); 33 } 34 return _DataManager.ToSharedRef(); 35 } 36 42   //内部添加字典元素,保存强引用,外部调用接口 43 TSharedRef<FData> CreateData() 44 { 45 TSharedPtr<FData> _data = MakeShareable(new FData()); 46 _MyDatas.Add(_data->GetID(), _data); 47 return _data.ToSharedRef(); 48 } 49 50 private: 51 static TSharedPtr<FDataManager> _DataManager; 52 53 TMap<uint64, TSharedPtr<FData>> _MyDatas; 54 55 56 }; 57 58 59 class FCharacter { 60 61 public: 62 63 //弱引用 64 void SetNewData(TSharedRef<FData> CurrentNewData) { 65 _Data = CurrentNewData; 66 } 67 void SetNewName(FString NewName) { 68 if (IsValid()) 69 { 70 _Data.Pin()->PlayerName = NewName; 71 } 72 } 73 74 FORCEINLINE bool IsValid() { 75 return _Data.IsValid(); 76 } 77 private: 78 TWeakPtr<FData> _Data; 79 }; 80 81 //假设一个场景 82 void m_NewMain() { 83 FCharacter* _FCharacter = new FCharacter();89 _FCharacter->SetNewData(FDataManager::Get()->CreateData()); 90 _FCharacter->SetNewName("杨涛"); 91 }

 

 暴露指针注意事项

一般来说不推荐直接暴露指针给用户,很危险,动不动就野了

//数据
class FData :public IMyID ,TSharedFromThis<FData>
{
public:
    FData()
    {
        _health = 100.0f;
        bDeath = 0;
        PlayerName = TEXT("yuyang");
    };
public:
    float _health;
    uint8 bDeath : 1;
    FName PlayerName;
};

 

 

class FDataManager {
public:

    FData* CreateDataNoProtect() {
        TSharedPtr<FData> _data = MakeShareable(new FData());
        _MyDatas02.Add(_data->GetID(), _data);
        return _data.Get();
    }

private:
    TMap<uint64, FData*> _MyDatas02;
};

 

 在暴露指针的同时,如果又new了强指针指向这块数据的话,就会出现野指针。

一般来说,是打包成智能引用,或者不直接暴露。

//假设一个场景
void m_NewMain() {
    FCharacter* _FCharacter = new FCharacter();
    {
        FData* _datanoprotect = FDataManager::Get()->CreateDataNoProtect();
        _FCharacter->SetNewData(_datanoprotect->AsShared());
        
    };
    _FCharacter->SetNewData(FDataManager::Get()->CreateData());
    _FCharacter->SetNewName("杨涛");
}

 

在将全局变量在函数里进行操作时,最好用new一个指针来赋值,这样不会随着离开作用域析构的时候导致野指针。

×

 

 

 √

 

另外,不支持C++原生的转换方式。

 

posted on 2020-07-07 17:35  moushen  阅读(837)  评论(0)    收藏  举报