您的位置:首页 > 理论基础 > 数据结构算法

3D游戏引擎底层数据结构的封装之Array

2017-01-17 17:41 148 查看
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

我写过一本关于比较简单的3D游戏引擎的封装,在这里给读者介绍一下,关于商业引擎中的底层封装,很多人可能会问,引擎已经为我们封装好了,拿过来用就可以了,不需要再自己封装了,这个没有错。引擎也是人写的,为什么人家能写,你写不了。难道我们就一直做一个逻辑程序员?虚幻引擎在开发游戏产品时,为了提升在移动端的性能,需要对底层做修改,如果我们对最基本的数据结构都搞不定,谈何修改底层?总之,学习一定要深入进行,不能只浮在表面。接下来的系列文章就是告诉读者,商业引擎的底层是如何封装的,我们自己如何封装类似STL模版中的数据结构,掌握了它们就等于迈出了学习引擎底层的第一步,首先介绍的是Array数组或者说是列表。

使用数组或者列表需要申请内存,内存的申请,释放可以直接调用Memory内存的类,在这里,我们将其封装了一下,封装内容如下所示:

__forceinline void*
operator new(size_t size)
{
return Memory::Alloc(Memory::ObjectHeap, size);
}

__forceinline void*
operator new(size_t size, const std::nothrow_t& noThrow)
{
return Memory::Alloc(Memory::ObjectHeap, size);
}

__forceinline void*
operator new[](size_t size)
{
return Memory::Alloc(Memory::ObjectArrayHeap, size);
}

__forceinline void*
operator new[](size_t size, const std::nothrow_t& noThrow)
{
return Memory::Alloc(Memory::ObjectArrayHeap, size);
}

__forceinline void
operator delete(void* p)
{
Memory::Free(Memory::ObjectHeap, p);
}

__forceinline void
operator delete[](void* p)
{
Memory::Free(Memory::ObjectArrayHeap, p);
}
#endif

#define n_new(type) new type
#define n_new_array(type,size) new type[size]
#define n_delete(ptr) delete ptr
#define n_delete_array(ptr) delete[] ptr

上面就是封装的关于内存存储,new一个空间和删除一个空间,以及一个宏定义。其实封装的意义在于一旦函数出现问题,只要修改对应的接口即可。函数封装中经常使用,一些变量定义,这个一般实用typedef定义。区别的好处是用于区分系统自己的:定义类型如下所示:

typedef unsigned long  ulong;
typedef unsigned int   uint;
typedef unsigned short ushort;
typedef unsigned char  uchar;
typedef unsigned char  ubyte;

typedef int IndexT;     // the index type
typedef int SizeT;      // the size type
static const int InvalidIndex = -1;

#define N_ARGB(a,r,g,b) ((uint)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define N_RGBA(r,g,b,a) N_ARGB(a,r,g,b)
#define N_XRGB(r,g,b)   N_ARGB(0xff,r,g,b)
#define N_COLORVALUE(r,g,b,a) N_RGBA((uint)((r)*255.f),(uint)((g)*255.f),(uint)((b)*255.f),(uint)((a)*255.f))

接下来开始着手定义Array的内容了,在开发中我们经常使用数组或者说是队列的接口,但是大家都是直接使用系统自带的。系统是如何实现的一概不知,下面就告诉读者如何自己去封装队列。自己封装就要什么都要考虑清楚,比如申请内存,获取队列大小,判断队列是否为空,插入队列元素,重置队列,,填充队列,清除队列,破坏队列,拷贝队列等等。这些接口系统自己的也有,我们自己封装的,对于需求我们可以随意更改。扩展性非常好,性能也是可以保证的。还有我们自己封装的队列并不是固定使用某个类型,对于这样的需求,最好的解决办法是使用模版template,模版的使用在游戏封装中使用的非常广泛,因为它可以做到通用。在这里告诉开发者封装队列,一是可以加深读者对队列的理解,另一个是告诉读者自己封装并不难的,别人能实现的技术,作为我们也是可以实现的。在这里我们分两部分:第一部分是关于Array的定义完整的函数代码如下所示:

template<class TYPE> class Array
{
public:
/// define iterator
typedef TYPE* Iterator;

/// constructor with default parameters
Array();
/// constuctor with initial size and grow size
Array(SizeT initialCapacity, SizeT initialGrow);
/// constructor with initial size, grow size and initial values
Array(SizeT initialSize, SizeT initialGrow, const TYPE& initialValue);
/// copy constructor
Array(const Array<TYPE>& rhs);
/// destructor
~Array();

/// assignment operator
void operator=(const Array<TYPE>& rhs);
/// [] operator
TYPE& operator[](IndexT index) const;
/// equality operator
bool operator==(const Array<TYPE>& rhs) const;
/// inequality operator
bool operator!=(const Array<TYPE>& rhs) const;
/// convert to "anything"
template<typename T> T As() const;

/// append element to end of array
void Append(const TYPE& elm);
/// append the contents of an array to this array
void AppendArray(const Array<TYPE>& rhs);
/// increase capacity to fit N more elements into the array
void Reserve(SizeT num);
/// get number of elements in array
SizeT Size() const;
/// get overall allocated size of array in number of elements
SizeT Capacity() const;
/// return reference to first element
TYPE& Front() const;
/// return reference to last element
TYPE& Back() const;
/// return true if array empty
bool IsEmpty() const;
/// erase element at index, keep sorting intact
void EraseIndex(IndexT index);
/// erase element pointed to by iterator, keep sorting intact
Iterator Erase(Iterator iter);
/// erase element at index, fill gap by swapping in last element, destroys sorting!
void EraseIndexSwap(IndexT index);
/// erase element at iterator, fill gap by swapping in last element, destroys sorting!
Iterator EraseSwap(Iterator iter);
/// insert element before element at index
void Insert(IndexT index, const TYPE& elm);
/// insert element into sorted array, return index where element was included
IndexT InsertSorted(const TYPE& elm);
/// insert element at the first non-identical position, return index of inclusion position
IndexT InsertAtEndOfIdenticalRange(IndexT startIndex, const TYPE& elm);
/// test if the array is sorted, this is a slow operation!
bool IsSorted() const;
/// clear array (calls destructors)
void Clear();
/// reset array (does NOT call destructors)
void Reset();
/// return iterator to beginning of array
Iterator Begin() const;
/// return iterator to end of array
Iterator End() const;
/// find identical element in array, return iterator
Iterator Find(const TYPE& elm) const;
/// find identical element in array, return index, InvalidIndex if not found
IndexT FindIndex(const TYPE& elm) const;
/// fill array range with element
void Fill(IndexT first, SizeT num, const TYPE& elm);
/// clear contents and preallocate with new attributes
void Realloc(SizeT capacity, SizeT grow);
/// returns new array with elements which are not in rhs (slow!)
Array<TYPE> Difference(const Array<TYPE>& rhs);
/// sort the array
void Sort();
/// do a binary search, requires a sorted array
IndexT BinarySearchIndex(const TYPE& elm) const;

private:
/// destroy an element (call destructor without freeing memory)
void Destroy(TYPE* elm);
/// copy content
void Copy(const Array<TYPE>& src);
/// delete content
void Delete();
/// grow array
void Grow();
/// grow array to target size
void GrowTo(SizeT newCapacity);
/// move elements, grows array if needed
void Move(IndexT fromIndex, IndexT toIndex);

static const SizeT MinGrowSize = 16;
static const SizeT MaxGrowSize = 65536; // FIXME: big grow size needed for mesh tools
SizeT grow;                             // grow by this number of elements if array exhausted
SizeT capacity;                         // number of elements allocated
SizeT size;                             // number of elements in array
TYPE* elements;                         // pointer to element array
};

定义的类型在前面已经给读者封装了,后面就是第二部分,函数实现的内容,这些函数读者可以直接拿去使用,完整的代码如下所示:

template<class TYPE>
Array<TYPE>::Array() :
grow(8),
capacity(0),
size(0),
elements(0)
{
// empty
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
Array<TYPE>::Array(SizeT _capacity, SizeT _grow) :
grow(_grow),
capacity(_capacity),
size(0)
{
if (0 == this->grow)
{
this->grow = 16;
}
if (this->capacity > 0)
{
this->elements = n_new_array(TYPE, this->capacity);
}
else
{
this->elements = 0;
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
Array<TYPE>::Array(SizeT initialSize, SizeT _grow, const TYPE& initialValue) :
grow(_grow),
capacity(initialSize),
size(initialSize)
{
if (0 == this->grow)
{
this->grow = 16;
}
if (initialSize > 0)
{
this->elements = n_new_array(TYPE, this->capacity);
IndexT i;
for (i = 0; i < initialSize; i++)
{
this->elements[i] = initialValue;
}
}
else
{
this->elements = 0;
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Copy(const Array<TYPE>& src)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(0 == this->elements);
#endif

this->grow = src.grow;
this->capacity = src.capacity;
this->size = src.size;
if (this->capacity > 0)
{
this->elements = n_new_array(TYPE, this->capacity);
IndexT i;
for (i = 0; i < this->size; i++)
{
this->elements[i] = src.elements[i];
}
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Delete()
{
this->grow = 0;
this->capacity = 0;
this->size = 0;
if (this->elements)
{
n_delete_array(this->elements);
this->elements = 0;
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Destroy(TYPE* elm)
{
elm->~TYPE();
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
Array<TYPE>::Array(const Array<TYPE>& rhs) :
grow(0),
capacity(0),
size(0),
elements(0)
{
this->Copy(rhs);
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
Array<TYPE>::~Array()
{
this->Delete();
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Realloc(SizeT _capacity, SizeT _grow)
{
this->Delete();
this->grow = _grow;
this->capacity = _capacity;
this->size = 0;
if (this->capacity > 0)
{
this->elements = n_new_array(TYPE, this->capacity);
}
else
{
this->elements = 0;
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::operator=(const Array<TYPE>& rhs)
{
if (this != &rhs)
{
if ((this->capacity > 0) && (rhs.size <= this->capacity))
{
// source array fits into our capacity, copy in place
n_assert(0 != this->elements);
IndexT i;
for (i = 0; i < rhs.size; i++)
{
this->elements[i] = rhs.elements[i];
}

// properly destroy remaining original elements
for (; i < this->size; i++)
{
this->Destroy(&(this->elements[i]));
}
this->grow = rhs.grow;
this->size = rhs.size;
}
else
{
// source array doesn't fit into our capacity, need to reallocate
this->Delete();
this->Copy(rhs);
}
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::GrowTo(SizeT newCapacity)
{
TYPE* newArray = n_new_array(TYPE, newCapacity);
if (this->elements)
{
// copy over contents
IndexT i;
for (i = 0; i < this->size; i++)
{
newArray[i] = this->elements[i];
}

// discard old array and update contents
n_delete_array(this->elements);
}
this->elements  = newArray;
this->capacity = newCapacity;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Grow()
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->grow > 0);
#endif

SizeT growToSize;
if (0 == this->capacity)
{
growToSize = this->grow;
}
else
{
// grow by half of the current capacity, but never more then MaxGrowSize
SizeT growBy = this->capacity >> 1;
if (growBy == 0)
{
growBy = MinGrowSize;
}
else if (growBy > MaxGrowSize)
{
growBy = MaxGrowSize;
}
growToSize = this->capacity + growBy;
}
this->GrowTo(growToSize);
}

//------------------------------------------------------------------------------
/**
30-Jan-03   floh    serious bugfixes!
07-Dec-04	jo		bugfix: neededSize >= this->capacity => neededSize > capacity
*/
template<class TYPE> void
Array<TYPE>::Move(IndexT fromIndex, IndexT toIndex)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements);
n_assert(fromIndex < this->size);
#endif

// nothing to move?
if (fromIndex == toIndex)
{
return;
}

// compute number of elements to move
SizeT num = this->size - fromIndex;

// check if array needs to grow
SizeT neededSize = toIndex + num;
while (neededSize > this->capacity)
{
this->Grow();
}

if (fromIndex > toIndex)
{
// this is a backward move
IndexT i;
for (i = 0; i < num; i++)
{
this->elements[toIndex + i] = this->elements[fromIndex + i];
}

// destroy remaining elements
for (i = (fromIndex + i) - 1; i < this->size; i++)
{
this->Destroy(&(this->elements[i]));
}
}
else
{
// this is a forward move
int i;  // NOTE: this must remain signed for the following loop to work!!!
for (i = num - 1; i >= 0; --i)
{
this->elements[toIndex + i] = this->elements[fromIndex + i];
}

// destroy freed elements
for (i = int(fromIndex); i < int(toIndex); i++)
{
this->Destroy(&(this->elements[i]));
}
}

// adjust array size
this->size = toIndex + num;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Append(const TYPE& elm)
{
// grow allocated space if exhausted
if (this->size == this->capacity)
{
this->Grow();
}
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements);
#endif
this->elements[this->size++] = elm;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::AppendArray(const Array<TYPE>& rhs)
{
IndexT i;
SizeT num = rhs.Size();
for (i = 0; i < num; i++)
{
this->Append(rhs[i]);
}
}

//------------------------------------------------------------------------------
/**
This increases the capacity to make room for N elements. If the
number of elements is known before appending the elements, this
method can be used to prevent reallocation. If there is already
enough room for N more elements, nothing will happen.

NOTE: the functionality of this method has been changed as of 26-Apr-08,
it will now only change the capacity of the array, not its size.
*/
template<class TYPE> void
Array<TYPE>::Reserve(SizeT num)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(num > 0);
#endif
SizeT neededCapacity = this->size + num;
if (neededCapacity > this->capacity)
{
this->GrowTo(neededCapacity);
}
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> SizeT
Array<TYPE>::Size() const
{
return this->size;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> SizeT
Array<TYPE>::Capacity() const
{
return this->capacity;
}

//------------------------------------------------------------------------------
/**
Access an element. This method will NOT grow the array, and instead do
a range check, which may throw an assertion.
*/
template<class TYPE> TYPE&
Array<TYPE>::operator[](IndexT index) const
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (index < this->size));
#endif
return this->elements[index];
}

//------------------------------------------------------------------------------
/**
The equality operator returns true if all elements are identical. The
TYPE class must support the equality operator.
*/
template<class TYPE> bool
Array<TYPE>::operator==(const Array<TYPE>& rhs) const
{
if (rhs.Size() == this->Size())
{
IndexT i;
SizeT num = this->Size();
for (i = 0; i < num; i++)
{
if (!(this->elements[i] == rhs.elements[i]))
{
return false;
}
}
return true;
}
else
{
return false;
}
}

//------------------------------------------------------------------------------
/**
The inequality operator returns true if at least one element in the
array is different, or the array sizes are different.
*/
template<class TYPE> bool
Array<TYPE>::operator!=(const Array<TYPE>& rhs) const
{
return !(*this == rhs);
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> TYPE&
Array<TYPE>::Front() const
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (this->size > 0));
#endif
return this->elements[0];
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> TYPE&
Array<TYPE>::Back() const
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (this->size > 0));
#endif
return this->elements[this->size - 1];
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> bool
Array<TYPE>::IsEmpty() const
{
return (this->size == 0);
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::EraseIndex(IndexT index)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (index < this->size));
#endif
if (index == (this->size - 1))
{
// special case: last element
this->Destroy(&(this->elements[index]));
this->size--;
}
else
{
this->Move(index + 1, index);
}
}

//------------------------------------------------------------------------------
/**
NOTE: this method is fast but destroys the sorting order!
*/
template<class TYPE> void
Array<TYPE>::EraseIndexSwap(IndexT index)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (index < this->size));
#endif

// swap with last element, and destroy last element
IndexT lastElementIndex = this->size - 1;
if (index < lastElementIndex)
{
this->elements[index] = this->elements[lastElementIndex];
}
this->Destroy(&(this->elements[lastElementIndex]));
this->size--;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> typename Array<TYPE>::Iterator
Array<TYPE>::Erase(typename Array<TYPE>::Iterator iter)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (iter >= this->elements) && (iter < (this->elements + this->size)));
#endif
this->EraseIndex(IndexT(iter - this->elements));
return iter;
}

//------------------------------------------------------------------------------
/**
NOTE: this method is fast but destroys the sorting order!
*/
template<class TYPE> typename Array<TYPE>::Iterator
Array<TYPE>::EraseSwap(typename Array<TYPE>::Iterator iter)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(this->elements && (iter >= this->elements) && (iter < (this->elements + this->size)));
#endif
this->EraseSwapIndex(IndexT(iter - this->elements));
return iter;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> void
Array<TYPE>::Insert(IndexT index, const TYPE& elm)
{
#if NEBULA3_BOUNDSCHECKS
n_assert(index <= this->size);
#endif
if (index == this->size)
{
// special case: append element to back
this->Append(elm);
}
else
{
this->Move(index, index + 1);
this->elements[index] = elm;
}
}

//------------------------------------------------------------------------------
/**
The current implementation of this method does not shrink the
preallocated space. It simply sets the array size to 0.
*/
template<class TYPE> void
Array<TYPE>::Clear()
{
IndexT i;
for (i = 0; i < this->size; i++)
{
this->Destroy(&(this->elements[i]));
}
this->size = 0;
}

//------------------------------------------------------------------------------
/**
This is identical with Clear(), but does NOT call destructors (it just
resets the size member. USE WITH CARE!
*/
template<class TYPE> void
Array<TYPE>::Reset()
{
this->size = 0;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> typename Array<TYPE>::Iterator
Array<TYPE>::Begin() const
{
return this->elements;
}

//------------------------------------------------------------------------------
/**
*/
template<class TYPE> typename Array<TYPE>::Iterator
Array<TYPE>::End() const
{
return this->elements + this->size;
}

//------------------------------------------------------------------------------
/**
Find element in array, return iterator, or 0 if element not
found.
@param  elm     element to find
@return         element iterator, or 0 if not found
*/
template<class TYPE> typename Array<TYPE>::Iterator
Array<TYPE>::Find(const TYPE& elm) const
{
IndexT index;
for (index = 0; index < this->size; index++)
{
if (this->elements[index] == elm)
{
return &(this->elements[index]);
}
}
return 0;
}

//------------------------------------------------------------------------------
/**
Find element in array, return element index, or InvalidIndex if element not
found.
@param  elm     element to find
@return         index to element, or InvalidIndex if not found
*/
template<class TYPE> IndexT
Array<TYPE>::FindIndex(const TYPE& elm) const
{
IndexT index;
for (index = 0; index < this->size; index++)
{
if (this->elements[index] == elm)
{
return index;
}
}
return InvalidIndex;
}

//------------------------------------------------------------------------------
/**
Fills an array range with the given element value. Will grow the
array if necessary
@param  first   index of first element to start fill
@param  num     num elements to fill
@param  elm     fill value
*/
template<class TYPE> void
Array<TYPE>::Fill(IndexT first, SizeT num, const TYPE& elm)
{
if ((first + num) > this->size)
{
this->GrowTo(first + num);
}
IndexT i;
for (i = first; i < (first + num); i++)
{
this->elements[i] = elm;
}
}

//------------------------------------------------------------------------------
/**
Returns a new array with all element which are in rhs, but not in this.
Carefull, this method may be very slow with large arrays!
@todo this method is broken, check test case to see why!
*/
template<class TYPE> Array<TYPE>
Array<TYPE>::Difference(const Array<TYPE>& rhs)
{
Array<TYPE> diff;
IndexT i;
SizeT num = rhs.Size();
for (i = 0; i < num; i++)
{
if (0 == this->Find(rhs[i]))
{
diff.Append(rhs[i]);
}
}
return diff;
}

//------------------------------------------------------------------------------
/**
Sorts the array. This just calls the STL sort algorithm.
*/
template<class TYPE> void
Array<TYPE>::Sort()
{
std::sort(this->Begin(), this->End());
}

//------------------------------------------------------------------------------
/**
Does a binary search on the array, returns the index of the identical
element, or InvalidIndex if not found
*/
template<class TYPE> IndexT
Array<TYPE>::BinarySearchIndex(const TYPE& elm) const
{
SizeT num = this->Size();
if (num > 0)
{
IndexT half;
IndexT lo = 0;
IndexT hi = num - 1;
IndexT mid;
while (lo <= hi)
{
if (0 != (half = num/2))
{
mid = lo + ((num & 1) ? half : (half - 1));
if (elm < this->elements[mid])
{
hi = mid - 1;
num = num & 1 ? half : half - 1;
}
else if (elm > this->elements[mid])
{
lo = mid + 1;
num = half;
}
else
{
return mid;
}
}
else if (0 != num)
{
if (elm != this->elements[lo])
{
return InvalidIndex;
}
else
{
return lo;
}
}
else
{
break;
}
}
}
return InvalidIndex;
}

//------------------------------------------------------------------------------
/**
This tests, whether the array is sorted. This is a slow operation
O(n).
*/
template<class TYPE> bool
Array<TYPE>::IsSorted() const
{
if (this->size > 1)
{
IndexT i;
for (i = 0; i < this->size - 1; i++)
{
if (this->elements[i] > this->elements[i + 1])
{
return false;
}
}
}
return true;
}

//------------------------------------------------------------------------------
/**
This inserts an element at the end of a range of identical elements
starting at a given index. Performance is O(n). Returns the index
at which the element was added.
*/
template<class TYPE> IndexT
Array<TYPE>::InsertAtEndOfIdenticalRange(IndexT startIndex, const TYPE& elm)
{
IndexT i = startIndex + 1;
for (; i < this->size; i++)
{
if (this->elements[i] != elm)
{
this->Insert(i, elm);
return i;
}
}

// fallthrough: new element needs to be appended to end
this->Append(elm);
return (this->Size() - 1);
}

//------------------------------------------------------------------------------
/**
This inserts the element into a sorted array. Returns the index
at which the element was inserted.
*/
template<class TYPE> IndexT
Array<TYPE>::InsertSorted(const TYPE& elm)
{
SizeT num = this->Size();
if (num == 0)
{
// array is currently empty
this->Append(elm);
return this->Size() - 1;
}
else
{
IndexT half;
IndexT lo = 0;
IndexT hi = num - 1;
IndexT mid;
while (lo <= hi)
{
if (0 != (half = num/2))
{
mid = lo + ((num & 1) ? half : (half - 1));
if (elm < this->elements[mid])
{
hi = mid - 1;
num = num & 1 ? half : half - 1;
}
else if (elm > this->elements[mid])
{
lo = mid + 1;
num = half;
}
else
{
// element already exists at [mid], append the
// new element to the end of the range
return this->InsertAtEndOfIdenticalRange(mid, elm);
}
}
else if (0 != num)
{
if (elm < this->elements[lo])
{
this->Insert(lo, elm);
return lo;
}
else if (elm > this->elements[lo])
{
this->Insert(lo + 1, elm);
return lo + 1;
}
else
{
// element already exists at [low], append
// the new element to the end of the range
return this->InsertAtEndOfIdenticalRange(lo, elm);
}
}
else
{
#if NEBULA3_BOUNDSCHECKS
n_assert(0 == lo);
#endif
this->Insert(lo, elm);
return lo;
}
}
if (elm < this->elements[lo])
{
this->Insert(lo, elm);
return lo;
}
else if (elm > this->elements[lo])
{
this->Insert(lo + 1, elm);
return lo + 1;
}
else
{
// can't happen(?)
}
}
// can't happen
n_error("Array::InsertSorted: Can't happen!");
return InvalidIndex;
}

因为这个是商业引擎使用的代码,考虑的比较完整,通过这个封装也是告诉读者,引擎内部的实现原理。平时我们自己写代码并没有考虑的这么周到,希望通过这些知识的学习帮你把数据结构重新复习一下,把基础知识掌握好,你才能腾飞。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: