你的浏览器不支持canvas

让我们携手共创奇迹!

TArray数组

Date: Author: LBD

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自WindCrazy

这是一篇关于UE中TArray数组的文章

一,TArray 的创建、初始化、赋值

TArray 的创建、初始化、赋值的主要形式如下。其中,列表既可以用作构造函数的参数,也可以用来初始化和赋值。Init() 可以在TArray 在放置若干个相同元素。

TArray<FString> Arr;
//Arr is []

TArray<FString> SecArr({ TEXT("Are"), TEXT("you"), TEXT("OK") });
//SecArr is [Are you OK]

TArray<FString> ThirdArr = { TEXT("Are"), TEXT("you"), TEXT("OK") };
//ThirdArr is [Are you OK]

ThirdArr.Init(TEXT("Hello"), 3);
//ThirdArr is [Hello Hello Hello]

Arr = { TEXT("Hello"), TEXT("World") };
//Arr is [Hello World]

AddUninitialized

AddUninitialized增加一个没有构造的元素(可能会扩容),他主要是改变TArray类中一个成员:ArrayNum的数值(只有当加完之后的Num值比ArrayMax值大,才会进行扩容)。使用FMemory::Memcpy函数会将数组数据写入,但是不会改变TArray对象的任何成员,因此一般追加数组数据会先AddUninitialized然后进行FMemory::Memcpy数据写入。否则使用Num函数获取数组长度时,是错误的。

protected:
    ElementAllocatorType AllocatorInstance;
    SizeType             ArrayNum;
    SizeType             ArrayMax;
FORCEINLINE SizeType AddUninitialized(SizeType Count)
{
   const USizeType OldNum = (USizeType)ArrayNum;
   const USizeType NewNum = OldNum + (USizeType)Count;
   ArrayNum = (SizeType)NewNum;

   if ((USizeType)Count > (USizeType)ArrayMax - OldNum)
   {
      ResizeGrow((SizeType)OldNum);//扩容
   }

   return OldNum;
}

使用示例:

int32 OldSize = UncompressedDataQueue.AddUninitialized(UncompressedDataSize);
FMemory::Memcpy(UncompressedDataQueue.GetData() + OldSize, UncompressedData.GetData(), UncompressedDataSize);

Append

这个方法有好几个,但都殊途同归

左值引用版:获取数组长度后直接用FMemory::Memcpy在后面追加数据

其中TIsBitwiseConstructible<DestinationElementType, SourceElementType>::Value会返回true或者false,在SourceElementType可以转换成DestinationElementType时会返回true,否则返回false,比如TIsBitwiseConstructible<const int*, int* >::Value == true TIsBitwiseConstructible<int*, const int*>::Value == false,ConstructItems函数主要是用它查看是否需要使用构造函数去创建。

template <typename OtherElementType, typename OtherAllocatorType>
void Append(const TArray<OtherElementType, OtherAllocatorType>& Source)
{
    check((void*)this != (void*)&Source);

    SizeType SourceCount = Source.Num();

    // Do nothing if the source is empty.
    if (!SourceCount)
    {
       return;
    }

    // Allocate memory for the new elements.
    SizeType Pos = AddUninitialized(SourceCount);
    ConstructItems<ElementType>(GetData() + Pos, Source.GetData(), SourceCount);
}
template <typename DestinationElementType, typename SourceElementType, typename SizeType>
FORCEINLINE void ConstructItems(void* Dest, const SourceElementType* Source, SizeType Count)
{
    if constexpr (TIsBitwiseConstructible<DestinationElementType, SourceElementType>::Value)
    {
       if (Count)
       {
          FMemory::Memcpy(Dest, Source, sizeof(SourceElementType) * Count);
       }
    }
    else
    {
       while (Count)
       {
          new (Dest) DestinationElementType(*Source);
          ++(DestinationElementType*&)Dest;
          ++Source;
          --Count;
       }
    }
}

右值引用版本:使用FMemory::Memmove原数组数据被析构并且将ArrayNum也设为0

template <typename OtherElementType, typename OtherAllocator>
void Append(TArray<OtherElementType, OtherAllocator>&& Source)
{
    check((void*)this != (void*)&Source);

    SizeType SourceCount = Source.Num();

    if (!SourceCount)
    {
       return;
    }

    SizeType Pos = AddUninitialized(SourceCount);
    RelocateConstructItems<ElementType>(GetData() + Pos, Source.GetData(), SourceCount);
    Source.ArrayNum = 0;
}

其他都差不多不过有一个指针版本,是上述AddUninitialized示例的简化版:UncompressedDataQueue.Append(UncompressedData.GetData(), UncompressedDataSize);

void Append(const ElementType* Ptr, SizeType Count)
{
    check(Ptr != nullptr || Count == 0);

    SizeType Pos = AddUninitialized(Count);
    ConstructItems<ElementType>(GetData() + Pos, Ptr, Count);
}

对于本文内容有问题或建议的小伙伴,欢迎提出问题