本章源代码位于
UnrealEngine/Source/Runtime/Core/Containers/Array.h
Array
先看下TArray模板参数和几个成员变量。
// TArray.h 286
template<typename InElementType, typename InAllocator>
// 293
typedef typename InAllocator::SizeType SizeType;
typedef InElementType ElementType;
typedef InAllocator Allocator;
// 2615
ElementAllocatorType AllocatorInstance;
SizeType ArrayNum;
SizeType ArrayMax;
可以看到TArray需要指定存储的类型和内存分配器,这不是我们常见的模板,另一个在ContainersFwd.h,这个后面说。SizeType是TArray的容量,ElementType是TArray存储的类型,Allocator是TArray的内存分配器。
接下来从TArray构造函数开始看,直接上代码。
/**
* Constructor, initializes element number counters.
*/
FORCEINLINE TArray()
: ArrayNum(0)
, ArrayMax(AllocatorInstance.GetInitialCapacity())
{}
/**
* Constructor from a raw array of elements.
*
* @param Ptr A pointer to an array of elements to copy.
* @param Count The number of elements to copy from Ptr.
* @see Append
*/
FORCEINLINE TArray(const ElementType* Ptr, SizeType Count)
{
check(Ptr != nullptr || Count == 0);
CopyToEmpty(Ptr, Count, 0, 0);
}
template <typename OtherElementType>
explicit TArray(const TArrayView<OtherElementType>& Other);
/**
* Initializer list constructor
*/
TArray(std::initializer_list<InElementType> InitList)
{
// This is not strictly legal, as std::initializer_list's iterators are not guaranteed to be pointers, but
// this appears to be the case on all of our implementations. Also, if it's not true on a new implementation,
// it will fail to compile rather than behave badly.
CopyToEmpty(InitList.begin(), (SizeType)InitList.size(), 0, 0);
}
/**
* Copy constructor with changed allocator. Use the common routine to perform the copy.
*
* @param Other The source array to copy.
*/
template <typename OtherElementType, typename OtherAllocator>
FORCEINLINE explicit TArray(const TArray<OtherElementType, OtherAllocator>& Other)
{
CopyToEmpty(Other.GetData(), Other.Num(), 0, 0);
}
/**
* Copy constructor. Use the common routine to perform the copy.
*
* @param Other The source array to copy.
*/
FORCEINLINE TArray(const TArray& Other)
{
CopyToEmpty(Other.GetData(), Other.Num(), 0, 0);
}
/**
* Copy constructor. Use the common routine to perform the copy.
*
* @param Other The source array to copy.
* @param ExtraSlack Tells how much extra memory should be preallocated
* at the end of the array in the number of elements.
*/
FORCEINLINE TArray(const TArray& Other, SizeType ExtraSlack)
{
CopyToEmpty(Other.GetData(), Other.Num(), 0, ExtraSlack);
}
- 第一个无参构造函数只不过给
ArrayNum和ArrayMax把值赋为0,创建TArray默认会用这个。 - 第二个构造参数传入另外一个
TArray第一个元素的指针和容量。 - 第三个构造参数传入任何一种数组变量。
- 第四个构造参数使用了C++11新模板类型
initializer_list可以传入花括号样式的数组(类似于{1,2,3}这种样式)。 - 第五个构造参数可以传入一个
TArray与第三个构造参数不同的是第五个构造参数可以指定内存分配器。 - 第六个是拷贝构造传入需要复制的数组和一个需要扩展的数组大小(预分配)。
基本以上函数都调用了CopyToEmpty这个函数,来看下这个函数实现了什么。
// Array.h 2581
/**
* Copies data from one array into this array. Uses the fast path if the
* data in question does not need a constructor.
*
* @param Source The source array to copy
* @param PrevMax The previous allocated size
* @param ExtraSlack Additional amount of memory to allocate at
* the end of the buffer. Counted in elements. Zero by
* default.
*/
template <typename OtherElementType, typename OtherSizeType>
void CopyToEmpty(const OtherElementType* OtherData, OtherSizeType OtherNum, SizeType PrevMax, SizeType ExtraSlack)
{
SizeType NewNum = (SizeType)OtherNum;
checkf((OtherSizeType)NewNum == OtherNum, TEXT("Invalid number of elements to add to this array type: %llu"), (unsigned long long)NewNum);
checkSlow(ExtraSlack >= 0);
ArrayNum = NewNum;
if (OtherNum || ExtraSlack || PrevMax)
{
ResizeForCopy(NewNum + ExtraSlack, PrevMax);
ConstructItems<ElementType>(GetData(), OtherData, OtherNum);
}
else
{
ArrayMax = AllocatorInstance.GetInitialCapacity();
}
}
// 2563
FORCENOINLINE void ResizeForCopy(SizeType NewMax, SizeType PrevMax)
{
if (NewMax)
{
NewMax = AllocatorInstance.CalculateSlackReserve(NewMax, sizeof(ElementType));
}
if (NewMax > PrevMax)
{
AllocatorInstance.ResizeAllocation(0, NewMax, sizeof(ElementType));
ArrayMax = NewMax;
}
else
{
ArrayMax = PrevMax;
}
}
第一个CopyToEmpty的参数有数组第一个元素、需要复制的元素数量、当前数组的最大容量(后面会用到)和需要拓展的预分配大小。
当OtherNum、ExtraSlack、PrevMax其中任意一个不为0那么就调用ResizeForCopy和ConstructItems这两个函数。
其中checkf和checkSlow是对代码进行静态分析时使用的宏,目前是空宏,没有实际意义。
// Array.h 303
static_assert(TIsSigned<SizeType>::Value, "TArray only supports signed index types");
// IsSigned.h
template <typename T>
struct TIsSigned
{
enum { Value = false };
};
template <> struct TIsSigned<int8> { enum { Value = true }; };
template <> struct TIsSigned<int16> { enum { Value = true }; };
template <> struct TIsSigned<int32> { enum { Value = true }; };
template <> struct TIsSigned<int64> { enum { Value = true }; };
template <typename T> struct TIsSigned<const T> { enum { Value = TIsSigned<T>::Value }; };
template <typename T> struct TIsSigned< volatile T> { enum { Value = TIsSigned<T>::Value }; };
template <typename T> struct TIsSigned<const volatile T> { enum { Value = TIsSigned<T>::Value }; };
UE使用static_assert在编译器判断TArray的索引是不是int8、int16、int32、int64其中一种类型,否者UE不允许使用TArray。