本文需要说明的是一种动态数组模板类(Array),可用于自定义的需要连续空间的容器,能有效得利用分配的空间,提供较高效的数组对象操作,和使用引用计数减少内存复制拷贝,内存头含有描述块,记录有效数据对象数量和总的内存对象大小。
特点如下:
(1)Array与std::vector或std::array不同。Array存储连续的对象,并且在对象内存前开辟一份数组描述块对数组进行描述。
(2)Array存储的数据数量是动态的,可以通过setLength调整,同时可以使用+、+=运算符向数组中追加数据。
(3)多个数组实例允许使用同一份数据内容,并且不会重复创建内容,这是该数组类最重要的特性以及主要涉及目的,即当从一个Array对象构造一个新的Array对象或者将他赋值给其他Array时不会产生新的内存申请和拷贝。多个Array的数据指向相同的内存区,并且通过引用计数管理该共同的内存区。
(4)当所有指向该内存区的Array对象都被销毁后,该内存会被释放。
(5)对一个空的Array追加内容时,他会自动开辟新的内存区用于存储新数据。
(6)对已经包含内容的Array追加或改写(通过[]下标运算)时,如果数组包含的内区还被其他数组引用,则会自动解除对原有内存区的引用并创建新的内存区存储新的数据。
使用注意:频繁的对数据进行追加操作是十分低效的,在已经确定要追加数量的情况下先调用setLength或者setCapacity设置最终长度或容量再进行追加将提高性能。
使用注意:Array设计只用于提供数据存取,因此数组中存放的内容不会被调用构造函数和析构函数。
具体的代码实现如下:
template
class Array
{
public:
typedef T TYPE;
struct ArrayDesc //内存描述模块
{
volatile int refer;//引用计数
volatile unsigned int capacity;//内存数量大小
volatile unsigned int length;//目前对象数量大小
};
protected:
T* m_ptr;
public:
Array()
{
m_ptr = NULL;
}
Array(const Array &another)
{
m_ptr = NULL;
operator = (another);
}
Array(const T* ptr, size_t length)
{
m_ptr = NULL;
setLength(length);
memcpy(m_ptr, ptr, length * sizeof(*ptr));
}
Array(size_t length)
{
m_ptr = NULL;
setLength(length);
}
~Array()
{
setLength(0);
}
//操作符=函数只是复制指针和增加引用计数(这里是减少了内存拷贝的,但在引用计数大于1时,为不影响其他引用对象,会使用内存拷贝)
inline Array& operator =(const Array &another)
{
if (m_ptr != another.m_ptr)//检查不是同一个对象
{
setLength(0);
//释放本对象的内存
if (another.m_ptr)
{
ArrayDesc *pDesc = ((ArrayDesc*) another.m_ptr) - 1;//获取另一对象的数组描述
lock_inc(pDesc->refer);//原子操作增加数组描述的引用计数
m_ptr = (T*) (pDesc + 1);//把另一对象的数组指针复制到本对象的数组指针
}
}
return *this;
}
inline Array operator +(const Array &another)
{
Array < T > result;
if (m_ptr || another.m_ptr)
{
size_t myCount = length();
size_t anotherCount = another.length();
result.setLength(myCount + anotherCount);//预留目标的内存空间
T* destPtr = result.m_ptr;
if (myCount > 0)
{
memcpy(destPtr, m_ptr, myCount * sizeof(*m_ptr));//拷贝本对象的数据内存
destPtr += myCount;
}
if (anotherCount > 0)
{
memcpy(destPtr, another.m_ptr, anotherCount * sizeof(*m_ptr));//拷贝另一个对象的数据内存
destPtr += myCount;
}
}
return result;//拷贝复制结果对象
}
//追加一个数据对象,返回的是新的数据对象的拷贝
inline Array operator +(const T &another)
{
size_t myCount = length();
Array < T > result(myCount + 1);//预留结果对象的内存空间
if (myCount > 0) memcpy(result.m_ptr, m_ptr, myCount * sizeof(*m_ptr));//拷贝本对象的数据到目标内存
result.m_ptr[myCount] = another;//拷贝追加的数据对象
return result;
}
//追加一个动态数组对象
inline Array& operator +=(const Array &another)
{
size_t len = another.length();
if (len > 0) cat(another.m_ptr, another.length());
return *this;
}
//追加一个数据对象,返回的是本对象
inline Array& operator +=(const T &another)
{
size_t myCount = length();
setLength(myCount + 1);
m_ptr[myCount] = another;
return *this;
}
//获取动态数组的数据索引上的数据对象的引用
inline T& operator [](size_t index)
{
if (!m_ptr) return *(T*) NULL;
ArrayDesc *pDesc = ((ArrayDesc*) m_ptr) - 1;
assert(index < pDesc->length);//检查索引不超出动态数组对象的长度
duplicate();
return m_ptr[index];
}
//获取动态数组的数据索引上的数据对象的引用(cosnt类型)
inline const T& operator [](size_t index) const
{
if (!m_ptr) return *(T*) NULL;
ArrayDesc * const pDesc = ((ArrayDesc* const ) m_ptr) - 1;
assert(index < pDesc->length);//检查索引不超出动态数组对象的长度
return m_ptr[index];
}
//判断数据对象是否是同一个对象
inline bool operator ==(const Array &another) const
{
return m_ptr == another.m_ptr;
}
//判断数据对象是否不是同一个对象
inline bool operator !=(const Array &another) const
{
return m_ptr != another.m_ptr;
}
//获取只读的内容数据指针
inline const T* ptr() const
{
return m_ptr;
}
//获取可写的内容数据指针
inline T* own_ptr()
{
duplicate();//复制本对象的数据内容到一个新的对象上,本对象就成了一个新的对象,旧的对象的数据内存需要在别处有管理
return m_ptr;
}
//获取本对象的数据数组长度
inline size_t length() const
{
if (!m_ptr) return 0;
ArrayDesc * const pDesc = ((ArrayDesc* const ) m_ptr) - 1;
return pDesc->length;
}
//追加一个或连续的多个数据对象到本数组对象的末尾
inline Array& cat(const T* ptr, size_t count)
{
return insert(length(), ptr, count);
}
//在指定索引位置插入一个或者连续的多个数据对象
inline Array& insert(size_t index, const T* ptr, size_t count)
{
if (count > 0)
{
ArrayDesc *pMyDesc = m_ptr ? ((ArrayDesc*) m_ptr) - 1 : NULL;
size_t myCount = pMyDesc ? pMyDesc->length : 0;
//本数组的数据对象数
assert(index <= myCount);
//判断目标是否是来自自己内存区域的数据,如果是来自自己内存区域的,那么可能
//在setLength后ptr指针失效,此时需要重新确定ptr的位置。
bool isMySelf = false;
size_t inMyPos = 0;
if (pMyDesc && (ptr >= m_ptr) && (ptr <= (m_ptr + pMyDesc->capacity)))
{
isMySelf = true;//目标对象的起始地址在本对象数组的内存区域内
inMyPos = ptr - m_ptr;//在内存区域的索引位置
if (inMyPos >= index) inMyPos += count;//重新定位需要拷贝的对象的起始位置ptr(跟setLength的实现有关)
}
setLength(myCount + count);//预留本对象的数据内存大小
//拷贝会被覆盖的数据内存到末尾(当内存发生局部重叠的时候,memmove保证拷贝的结果是正确的,memcpy不保证拷贝的结果的正确)
if (index < myCount) memmove(&m_ptr[index + count], &m_ptr[index], sizeof(*ptr) * (myCount - index));
//拷贝需要拷贝的数据对象到指定的位置
if (isMySelf) memcpy(&m_ptr[index], &m_ptr[inMyPos], count * sizeof(*m_ptr));
else memcpy(&m_ptr[index], ptr, count * sizeof(*m_ptr));
}
return *this;
}
inline Array& remove(size_t index, size_t count)
{
if (count > 0)
{
duplicate();//拷贝本数组对象到新的内存(引用计数大于1时才会复制拷贝,主要是不影响其他引用的对象)
ArrayDesc *pMyDesc = m_ptr ? ((ArrayDesc*) m_ptr) - 1 : NULL;
size_t myCount = pMyDesc ? pMyDesc->length : 0;
assert(index < myCount);
if (count > myCount - index) count = myCount - index;
size_t copyCount = myCount - index - count;
if (copyCount > 0) memcpy(&m_ptr[index], &m_ptr[index + count], sizeof(*m_ptr) * copyCount);
setLength(myCount - count);
}
return *this;
}
inline Array& setLength(const size_t length)
{
assert(length >= 0);
ArrayDesc *pDesc = NULL;
if (m_ptr) pDesc = ((ArrayDesc*) m_ptr) - 1;
if (length == 0)//回收本数组对象
{
if (pDesc)
{
if (lock_dec(pDesc->refer) <= 0) //引用计数为0,释放本数组对象的内存
free(pDesc);
m_ptr = NULL;
}
}
else if (pDesc)
{
if (length != pDesc->length)
{
//引用计数大于1时返回的是内存拷贝的数组对象(不影响其他的引用对象)
if (pDesc->refer > 1)
{
lock_dec(pDesc->refer);//减少本对象的引用计数
ArrayDesc *pNewDesc = (ArrayDesc*) calloc(1, sizeof(*pDesc) + sizeof(*m_ptr)
* (length + 1));//分配新的内存对象,calloc分配连续内存,初始化已分配的内存为0,返回数组
pNewDesc->capacity = (unsigned int) length;
pNewDesc->length = (unsigned int) length;
pNewDesc->refer = 1;
m_ptr = (T*) (pNewDesc + 1);//把本数组对象重新定位到新的数组对象内存
size_t lengthMin = pDesc->length < length ? pDesc->length : length;
memcpy(m_ptr, pDesc + 1, lengthMin * sizeof(*m_ptr));//拷贝本数组对象内存到新的对象的内存
}
else //引用计数为1是返回的就是本对象,有效长度和内存大小根据需求调整
{
if (length >= pDesc->capacity)//需要的内存大小大于本数组对象的包含的内存大小
{
pDesc = (ArrayDesc*) realloc(pDesc, sizeof(*pDesc) + sizeof(*m_ptr) * (length + 1));
//重新定位本数组对象的数据指针到新的内存地址
m_ptr = (T*) (pDesc + 1);
//把数组对象末尾的无效对象初始化
memset(&m_ptr[pDesc->length], 0, sizeof(*m_ptr) * (length - pDesc->capacity + 1));
pDesc->capacity = (unsigned int) length;//内存大小
pDesc->length = (unsigned int) length;//数组大小
}
else pDesc->length = (unsigned int) length;//截短本数组对象的有效长度
}
memset(&m_ptr[length], 0, sizeof(*m_ptr));//数组有效长度后面的那个对象初始化为0(在数组对象为字符串时有作用)
}
}
else //数组数据对象指针没有内存,则分配新的内存并把本数组对象数据指针重新定位到新的内存上
{
ArrayDesc *pNewDesc =
(ArrayDesc*) calloc(1, sizeof(*pDesc) + sizeof(*m_ptr) * (length + 1));
pNewDesc->capacity = (unsigned int) length;
pNewDesc->length = (unsigned int) length;
pNewDesc->refer = 1;
m_ptr = (T*) (pNewDesc + 1);
}
return *this;
}
inline size_t capacity() const
{
if (m_ptr)
{
ArrayDesc *pDesc = ((ArrayDesc*) m_ptr) - 1;
return pDesc->capacity;
}
return 0;
}
inline Array& setCapacity(size_t capacity)
{
size_t myCount = length();//容量必须大于数量。如果要进行清空操作,则请调用setLength(0)
if (capacity > myCount)
{
setLength(capacity);
ArrayDesc *pDesc = ((ArrayDesc*) m_ptr) - 1;
pDesc->length = myCount;
}
return *this;
}
protected:
inline void duplicate()//拷贝复制本数组对象到新的内存
{
if (m_ptr)
{
ArrayDesc *pDesc = ((ArrayDesc*)m_ptr) - 1;//记录本数组对象的描述块指针
if (pDesc->refer > 1)
{
ArrayDesc *pNewDesc = (ArrayDesc*)malloc(sizeof(*pDesc) + sizeof(*m_ptr) * (pDesc->capacity + 1));//多预留了一个数据对象的大小
pNewDesc->capacity = pDesc->capacity;
pNewDesc->length = pDesc->length;
pNewDesc->refer = 1;
m_ptr = (T*)(pNewDesc + 1);//把本数组对象重新定位到新的内存
memcpy( m_ptr,pDesc + 1, (pDesc->length + 1) * sizeof(*m_ptr));//拷贝本数组对象的原来的数据内存到新的数组对象内存
lock_dec(pDesc->refer);//减少原来内存的引用计数
}
}
}
};