虚幻中的容器—TArray

TArray 是UE4中最常用的容器类。其速度快、内存消耗小、安全性高。TArray 类型由两大属性定义:元素类型和可选分配器。

元素类型是存储在数组中的对象类型。TArray 被称为同质容器。换言之,其所有元素均完全为相同类型。单个 TArray 中不能存储不同类型的元素。

分配器常被省略,默认为最常用的分配器。其定义对象在内存中的排列方式;以及数组如何进行扩展,以容纳更多的元素。若默认行为不符合要求,可选取多种不同的分配器,或自行编写。此部分将稍后讨论。

Tarray 为数值类型。意味其与其他内置类型(如 int32 或 浮点)的处理方式相同。其设计时未考虑扩展问题,因此建议在实际操作中勿使用 新建(new) 和 删除(delete) 创建或销毁 TArray 实例。元素也为数值类型,为容器所拥有。TArray 被销毁时其中的元素也将被销毁。若在另一TArray中创建TArray变量,其元素将复制到新变量中,且不会共享状态。

创建和填充数组

创建一个Actor创建该函数

void InitIntArray()
{
TArray<int32> IntArray1;
TArray<int32> IntArray2;

int32 ArrayNum = IntArray1.Num();
int32 ArraySize = IntAllocatedSize();

//初始化为数组大小为五,值为10
IntArray1.Init(10,5);
IntArray2 = {1,2,3,4,5};

ArrayNum = IntArray1.Num();
ArraySize = IntArray1.GetAllocatedSized();

} 

Add和Emplace的区别

新元素添加到数组时,数组的分配器将根据需要分配内存。当前数组大小超出时,默认分配器将添加足够的内存,用于存储多个新元素。Add 和 Emplace 函数的多数效果相同,细微区别在于:

Add(或 Push)将元素类型的实例复制(或移动)到数组中。

Emplace 使用给定参数构建元素类型的新实例。

因此在 TArray<FString> 中,Add 将用字符串文字创建临时 FString,然后将该临时 FString 的内容移至容器内的新 FString 中;而 Emplace 将用字符串文字直接新建 FString。最终结果相同,但 Emplace 可避免创建临时文件。对于 FString 等非浅显数值类型而言,临时文件通常有害无益。

总体而言,Emplace 优于 Add,因此其可避免在调用点创建无需临时变量,并将此类变量复制或移动到容器中。根据经验,可将 Add 用于浅显类型,将 Emplace 用于其他类型。Emplace 的效率始终高于 Add,但 Add 的可读性可能更好。

void AddStrArray()
{
  TArray<FString> StrArr;

  StrArr.Add(TEXT("Hello"));
  StrArr.Emplace(TEXT("World"));

  StrArr.Add(TEXT("My"));
  StrArr.Add(TEXT("City"));
  StrArr.Add(TEXT("Shanxi"));
  StrArr.Add(TEXT("Well"));
}

定义一个结构体

UStruct()
struct AddStructInfo
{
GENERATED_BODY()

public:
  AddStructInfo()
  { UE_LOG(LogTemp,Warning,TEXT("我被初始化了0")); }

  AddStructInfo(int32 InHealth) : Healty(InHealth)
  { UE_LOG(LogTemp,Warning,TEXT("我被初始化了1")); } 
  
  ~AddStructInfo()
  { UE_LOG(LogTemp,Warning,TEXT("我被释放了")); }
  
  int32 Health = 100;
};

 在.h文件中声明该属性

public:
       TArray<AddStructInfo> StructArr;

 在这里创建两个方法用来调试,将该两个方法暴露在蓝图中进行调试

void AddStructArray()
{
  StructArray.Add(150);
}

void EmplaceStructArray()
{
  StructArray.Emplace(200);
}

 我们可以观察到当我们使用Add方法时  我们观察日志可以看到

LogTemp,Warning,我被初始化了1

LogTemp,Warning,我被释放了

当我们使用Emplace方法时我们观察日志可以看到

LogTemp,Warning,我被初始化了1 

当我们游戏结束时

LogTemp,Warning,我被释放了

由此可以看出Add需要一个临时变量将元素类型的实例复制(或移动)到数组中,然后释放临时对象。

而Emplace则是直接构建一个新的对象,只有当actor消亡的时候他才会释放

其他添加元素的方式

对Append的测试

写出该方法,同时将该方法暴露在蓝图中

void AppendStrArray()
{
  FString Arr[] = { TEXT("of"),TEXT("Tomorrow") }; 

  StrArr.Append()
  StrArr.Add(TEXT("Hello"));
  StrArr.Emplace(TEXT("World"));
  StrArr.Append(Arr, UE_ARRAY_COUNT(Arr));
}

通过打算点可以测试出

对AddUnique

使用该方法不出现两次相同的元素

void AddUniqueString()
{
   TArry<Fstring> StrArr = AppendStrArray();
    
   StrArr.AddUnique(TEXT("!"));
   StrArr.AddUnique(TEXT("!"));
}

dda

 该函数执行完之后只有一个感叹号

Insert方法的测试

void InsertString()
{
  TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!"};
  
  StrArr.Insert(TEXT("Brave"),1);
}

 打断点测试结果如下

SetNum 

void SetStringNum()
{
   TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!"};
   StrArr.SetNum(10);
   StrArr.SetNum(5);
}

结构体唯一性的判断

数组迭代实战

使用C++的范围(ranged-for)功能

UFUNTION(BlueprintCallable)
void LoopArray();

void LoopArray()
{
    FString JoinedStr1;
    FString JoinedStr2;
    TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };
    
    for(auto& Str : StrArr)
    {
       JoinedStr1 += Str;
       JOinedStr1 += TEXT(" ");
    }
    for(auto Str : StrArr)
    {
       JoinedStr2 += Str;
       JoinedStr2 += TEXT(" ");
    }
}

 通过打断点测试可以测出

 基于索引的常规迭代

void LoopArray2()
{ 
   FString JoinedStr;
   TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!"};
   for( int32 Index = 0; Index != StrArr.Num(); ++Index)
   {
      JoinedStr += StrArr[Index];
      JoinedStr += TEXT(" ");
   } 
}

通过数组迭代器类型控制迭代

void LoopArray3()
{ 
   FString JoinedStr;
   TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!"};
   for(auto It = StrArr.CreateConstInterator(); It; ++It)
   {
      JoinedStr += *It;
      JoinedStr += TEXT(" ");
   } 
}

移除数组中的元素

当正序移除其中元素时,TArray数组内部会进行重新排序导致移除的数据出错

这是第一种移除方式

void LoopArray_Remove()
{ 
  FString JoinedStr;
  TArray<FString> StrArr = {"Hello"," World","of","Tomorrow","!"}
  
  for (int32 Index = StrArr.Num()-1; Index >= 0 ; --Index)
  {
      if(TEXT("of") == StrArr[Index])
      {
          if(TEXT("of") == StrArr[Index])
          {
              StrArr.RemoveAt("Index"); 
          } 
          else
          {
              JoinedStr = StrArr[Index] + JoinedStr;
              JoinStr += StrArr[Index];
              JoinedStr += TEXT(" ");
          }

       }
   }
}

 这是第二种移除方式

void LoopArray_Remove()
{ 
  TArray<FString> StrArr = {"Hello","My"," World","of","Tomorrow","!"}
  TArray<int32> RemovedIndexArray
  
  for (int32 Index = StrArr.Num()-1; Index >= 0 ; --Index)
  {
      if(TEXT("of") == StrArr[Index])
      {
         RmovedIndexArray.Add(Index);
      }
      if(TEXT("of") == StrArr[Index])
      {
         RemovedIndexArray.Add(Index);
      }
   }
   for(int32 RemoveLoopIndex = 0; RemoveLoopIndex != RemovedIndexArray; ++RemovedLoopIndex)
   {
       StrArr.RemoveAt(RemovedIndexArray[RemovedLoopIndex])
   }
}

//注意一定要分帧执行
//WebSocket通信过程去创建新webSocket

常见排序

Sort函数

在此,数值按元素类型的 运算符< 排序。在FString中,此为忽略大小写的词典编纂比较。

void SortArray()
{ 
   TArray<FString> StrArr = { "aa","ab","AA","ab","ABC","BB","VE","CCCCC","AD","DDDDDA","AA" }

   StrArr.Sort();
}

字符串现在按长度排序。注意:与之前相比,数组中三个长度相同的字符串"Hello"、"Brave"和"World"的相对排序发生了变化。这是因为 Sort 不稳定,等值元素(因为断言只比较长度,所以此处字符串为等值)的相对排序无法保证。Sort 作为quicksort实现。

HeapSort 函数

无论是否使用二元谓词,均可用于执行堆排序。使用HeapSort函数与否,取决于特定数据与Sort函数相比时的排序效率。与 Sort 一样,HeapSort 也不稳定。若在上述范例中使用 HeapSort 而非 Sort,结果将如下所示(此例中结果相同):

void HeapArray()
{ 
   TArray<FString> StrArr = { "aa","ab","AA","ab","ABC","BB","VE","CCCCC","AD","DDDDDA","AA" }

   StrArr.Sort();
}

 StableSort 函数

用于在排序后保证等值元素的相对顺序。

void StableArray()
{ 
   TArray<FString> StrArr = { "aa","ab","AA","ab","ABC","BB","VE","CCCCC","AD","DDDDDA","AA" }

   Stable.Sort();
}

 

二元谓词排序实际应用 

void SortArray_Sort
{
   int32 OriginNum = 100;
   auto Lamba = [OriginNum](int32 InNum,int32 InAddNum)
   {
      int32 Max = InNum + InAddNum;

      Max += 100;
      
      return Max;
   };

   int32 CaculatedNum1 = LambdaMethod(10,2);
   
   int32 CaculatedNum2 = CalculateMethod(10,2);
}



int32 CalculateMethod(int32 InNum,int InAddNum)
{
    int32 Max = InNum + InAddNum;
    Max += TempNum;
    return Max;
}

void SortArray_Sort2
{ 
   TArray<FString> StrArr = { "CCCCC","BB","aa","ab","ABC","VE","AA","AD""AB","DDDDDA","AA" };
   StrArr.HeapSort([](const FString& A,const FString& B)
   {
      return A.Len < B.Len;
   });
}

 

 

void SortArray_Sort2
{ 
   TArray<FString> StrArr = { "CCCCC","BB","aa","ab","ABC","VE","AA","AD""AB","DDDDDA","AA" };
   StrArr.StableSort(const FString& A,const FString& B)
   {
      return A.Len < B.Len;
   }
}

 

字符串现在按长度排序。注意:与之前相比,数组中三个长度相同的字符串"Hello"、"Brave"和"World"的相对排序发生了变化。这是因为 Sort 不稳定,等值元素(因为断言只比较长度,所以此处字符串为等值)的相对排序无法保证。Sort 作为quicksort实现。

HeapSort 函数,无论是否使用二元谓词,均可用于执行堆排序。使用HeapSort函数与否,取决于特定数据与Sort函数相比时的排序效率。与 Sort 一样,HeapSort 也不稳定。

记录一个常用的方法

可以用在物品栏当中

UStruct()
struct SortStructInfo
{
GENERATED_BODY()

public:
  SortStructInfo() {}
  
  SortStructInfo(int32 InID,int32 InMoney)
  : ID(InID),Money(InMoney) { }
  
  int32 ID = 0;
  int32 Money = 0;
 
  bool operator == (const SortStructInfo& Other) const
  { return ID < Other.ID ? true : false; }
};
void SortStructArray_StableSort()
{
    TArray<SortStructInfo> MyTeams1;
    
    MyTeams1.Add( { 99,2 } );
    
    MyTeams1.Add( { 1,10} );

    MyTeams1.Add( { 66,3} );

    MyTeams1.Add( { 88,60} );
    
    TArray<SortStructInfo> MyTeams2;

    MyTeams2.Add( { 99,2 } );
    
    MyTeams2.Add( { 1,10} );

    MyTeams2.Add( { 66,3} );

    MyTeams2.Add( { 88,60} );

    MyTeams1.StableSort();
    
    MyTeams2.StableSort([](const SortStructInfo&A,const SortStructInfo& B)->bool{
    return A.Money< B.Money ? true : false;
    } );
}

 

数组查询演示

在引擎中进行调试,可以直观观察出。

void FindArray()
{
    TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };

    int32 ArraySize = StrArr.Num();

    FString* StrPtr = StrArr.GetData();

    bool bP0 = StrPtr[0] == "Hello";
    bool bP1 = StrPtr[1] == "World";
    bool bP2 = StrPtr[2] == "of";
    bool bP2 = StrPtr[3] == "of";
    
    //!
    FString MyTemp1 = *(StrPtr + 4)
    //Hello 4
    FString MyTemp2 = *StrPtr + TEXT("4");

    //false 
    bool bP2 = *(StrPtr+4) == "of";

    StrPtr[5];

    //越界
    StrArr[5];
}





void FindArray_Change()
{
    TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };

    StrArr.Append(StrArr2);
    StrArr.Append(StrArr2);
    StrArr.Append(StrArr2);
    StrArr.Append(StrArr2);

    FString* StrPtr = StrArr.GetData();

    bool bP0 = StrPtr[0] == "Hello";

    bool bP1 = StrPtr[1] == "World";

    bool bP2 = StrPtr[2] == "of";

    bool bP2 = StrPtr[3] == "of";

    bool bPZ0 = StrArr[0] == "Hello";

    bool bPZ1 = StrArr[1] == "World";

    bool bPZ2 = StrArr[2] == "of";

    bool bPZ2 = StrArr[3] == "of";
}


void FindArray_ElementSize()
{
    TArray<FString> StrArr = { "Hello","World","Of","Tomorrow","!" }; 

    uint32 ElementSize1 = StrArr.GetTypeSize();
      
    uint32 ElementSize2 = sizeof(FString);
  
    TArray<int32> IntArr = { 2,3,4,5,6 };
    uint32 ElementSize3 = IntArr.GetTypeSize();
    
    TArray<uint8> Uint8Arr = { 2,3,4,5,6 };
    uint32 ElementSize4 = Uint8Arr.GetTypeSize();

}

数组索引演示

void FindArray_Index()
{
    TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };
       
    FString Elem1 = StrArr[1];
    
    Elem1 += TEXT("10");

    FString& Elem1_X = StrArr[1];

    Elem1_X += TEXT("10");
}

void IndexArray_IsValid()
{ 
    TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };

    bool bValidM1 = StrArr.IsValidIndex(-1);
    
    bool bValied0 = StrArr.IsValidIndex(0);

    bool bValied1 = StrArr.IsValidIndex(1);

    bool bValied2 = StrArr.IsValidIndex(2);

    bool bValied3 = StrArr.IsValidIndex(3);

    bool bValied4 = StrArr.IsValidIndex(4);

    bool bValied5 = StrArr.IsValidIndex(5);

    bool bValied6 = StrArr.IsValidIndex(6);   
}

void IndexArray_Latest()
{
     TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };

     FString ElemEnd = StrArr.Last();

     FString ElemEnd0 = StrArr.Last(0);

     FString ElemEnd1 = StrArr.Last(1);

     FString ElemTop = StrArr.Top();
}
void IndexArray_Upper
{
    TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };
    
    StrArr[1] = StrArr[1].ToUpper();
}

Find函数的使用

void ContaionArray()
{
       TArray<FString> StrArr = { "12347756","33","44","Hello","World","of","Tomorrow","!" };
       
       bool Hello = StrArr.Contains(TEXT("Hello"));
       bool bGoodbye = StrArr.Contains(TEXT("Goodbye"));

       int32 bFind5Character = 0;
       bool bLen5 = StrArr.ContainsByPredicate([](const FString& Str) 
       {
            ++bFind5Character;

            UE_Log(LogTemp,Warning,TEXT("bLen5,[%d]"),)            

            return Str.Len() == 5;
       });

       int32 bFind6Character = 0;
       bool bLen6 = StrArr.ContainsByPredicate([](const FString& Str) 
       {
           ++bFind6Character;

           UE_Log(LogTemp,Warning,TEXT("bLen6,[%d]"),)       

           return Str.Len() == 6;
       });
}
void FindElementArray()
{
     TArray<FString> StrArr = { "Hello","World","of","Tomorrow","Hello","!" };

     int32 Index = -1;

     bool bIndex = StrArr.Find(TEXT("Hello"),Index);

     int32 LasetIndex = -1;

     bool bIndexFromIndex =  StrArr.FindLast(TEXT("Hello"),LastIndex);

     int32 Index2 = StrArr.Find(TEXT("Hello"));
     
     int32 IndexLast2 = StrArr.FindLast(TEXT("Hello"));
   
     int32 IndexNone = StrArr.Find(TEXT("None"));
  
     bool bIndexNone = INDEX_NONE == IndexNone;
   
}

 各类查询函数

void FindElemntByKey()
{
   TArray<FString> StrArr = { "Hello","World","of","Tomorrow","!" };
    
   int32 Index = StrArr.IndexOfByKey(TEXT("Hello"));
  
   int32 IndexP = StrArr.IndexofByPredicate([](const FString& Str) 
   {
   return Str.Contains(TEXT("r"))  
    });

}

移除操作

void RemoveElement()
{
   TArray<int32> ValArr;
   int32 Temp[] = { 10,20,30,5,10,15,20,25,30 };
   
   ValArr.Append(Temp,ValArr.Num());

   ValArr.Remove(20);

   ValArr.RemoveSingle(30);
 
   ValArr.RemoveAt(2);

   ValArr.Shrink();

   ValArr.RemoveAt(99);
}
void RemoveElement()
{
   TArray<int32> ValArr;
   int32 Temp[] = { 10,20,30,5,10,15,20,25,30 };
   
   ValArr.Append(Temp,9);


   ValArr.RemoveAll([](int32 Val)
   {
       return Val % 3 == 0;
   });

   ValArr.Empty();
   ValArr.Append(Temp,9);
   ValArr.RemoveSwap(2);

   ValArr.Empty();
   ValArr.Append(Temp,9);
   ValArr.RemoveSwap(2);

   ValArr.RemoveAllSwap([](int32 Val)
   {
       return Val % 3 == 0;
   });
}

 运算符

void OperateArray()
{
     TArray<int32> ValArr3;

     ValArr3.Add(1);

     ValArr3.Add(2);

     ValArr3.Add(3);

     auto VarArr4 = VarArr3;

     TArray<int32> ValArr5 = ValArr3;

     ValArr4[0] = 5;
 
     TArray<int32> ValArr6;
     ValArr6 += ValArr4;
     
     TArray<int32> ValArr7 = { 1,1,1 };
     ValArr7 = MoveTemp(ValArr6);
}
void OperateStrArray()
{
    TArray<FString> FlavorArr1;
	FlavorArr1.Emplace(TEXT("Chocolate"));
	FlavorArr1.Emplace(TEXT("Vanilla"));

	auto FlavorArr2 = FlavorArr1;

	bool bComparison1 = FlavorArr1 == FlavorArr2;

	for (auto& Str :FlavorArr2)
	{
		Str = Str.ToUpper();
	}

	bool bComparison2 = FlavorArr1 == FlavorArr2;

	Exchange(FlavorArr2[0], FlavorArr2[1]);

	bool bComparison3 = FlavorArr1 == FlavorArr2;
}

 Slack元素

void SlackArray()
{
    TArray<int32> SlackArray;

    int32 SlackNum = SlackArray.GetSlack();

    int32 Num = SlackArray.Num();
    int32 Max = SlackArray.Max();
  
    SlackArray().Add(1);

    SlackNum = SlackArray.GetSlack();

    int32 Num = SlackArray.Num();
    int32 Max = SlackArray.Max();

    SlackArray().Add(2);
    SlackArray().Add(3);
    SlackArray().Add(4);
    SlackArray().Add(5);
}
void EmptyArray()
{
    TArray<int32> SlackArray;
    SlackArray.Add(10);

    SlackArray.Empty(11);

    SlackArray.Empty();

	SlackArray.Empty(3);

	SlackArray.Add(1);
	SlackArray.Add(2);
	SlackArray.Add(3);

}

void ResetArray()
{
   TArray<int32> SlackArray;
   SlackArray.Add(1);
   SlackArray.Add(2);
   SlackArray.Add(3);

   SlackArray.Reset(0);

   SlackArray.Reset(10);

   SlackArray.Shrink();
}

 原始内存

void OriginArray()
{
    int32 SrcInts[] = { 2, 3, 5, 7 };
	TArray<int32> UninitInts;

	UninitInts.AddUninitialized(4);

	FMemory::Memcpy(UninitInts.GetData(), SrcInts, 4*sizeof(int32));

    TArray<FStructInfo> UninitStructs;
    UninitStructs.AddUninitialized(4); 
}
void OriginArray2()
{
    TArray<FString> UninitStrs;
	UninitStrs.Emplace(TEXT("A"));
	UninitStrs.Emplace(TEXT("D"));
	UninitStrs.InsertUninitialized(1, 2);
	new ((void*)(UninitStrs.GetData() + 1)) FString(TEXT("B"));
	new ((void*)(UninitStrs.GetData() + 2)) FString(TEXT("C"));
	// UninitStrs == ["A","B","C","D"]

    TArray<FStructInfo> UninitStructs;
	UninitStructs.Emplace(20);
	UninitStructs.Emplace(50);
	new ((void*)(UninitStructs.GetData() + 1)) FStructInfo(99);
	new ((void*)(UninitStructs.GetData() + 2)) FStructInfo(88);

    UninitStructs.Empty();
}
void ZeroArray()
{
    TArray<FStructInfo> SArr;
	SArr.AddZeroed();
}
void Test()
{
struct S
	{
		S(int32 InInt, void* InPtr, float InFlt)
			:Int(InInt)
			, Ptr(InPtr)
			, Flt(InFlt)
		{
		}
		int32 Int;
		void* Ptr;
		float Flt;
	};
	TArray<S> SArr;
	SArr.AddZeroed();
	// SArr == [{ Int:0, Ptr: nullptr, Flt:0.0f }]
    SArr.SetNumUninitialized(3);
	new ((void*)(SArr.GetData() + 1)) S(5, (void*)0x12345678, 3.14);
	new ((void*)(SArr.GetData() + 2)) S(2, (void*)0x87654321, 2.72);
	// SArr == [
	//   { Int:0, Ptr: nullptr,    Flt:0.0f  },
	//   { Int:5, Ptr:0x12345678, Flt:3.14f },
	//   { Int:2, Ptr:0x87654321, Flt:2.72f }
	// ]

	SArr.SetNumZeroed(5);
	// SArr == [
	//   { Int:0, Ptr: nullptr,    Flt:0.0f  },
	//   { Int:5, Ptr:0x12345678, Flt:3.14f },
	//   { Int:2, Ptr:0x87654321, Flt:2.72f },
	//   { Int:0, Ptr: nullptr,    Flt:0.0f  },
	//   { Int:0, Ptr: nullptr,    Flt:0.0f  }
	// 
    SArr.SetNumZeroed(5);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值