实际上这里谈到的类是ATL里面的类,位于头文件: atlsimpcoll.h
如果你去探寻,就会发现WTL实际上就是ATL,只不过它扩展了ATL的界面功能。WTL当然有许多自己的东西,但那是另外的问题了。
template <class T, class TEqual = CSimpleArrayEqualHelper< T > >
class CSimpleArray
{
public:
// Construction/destruction
CSimpleArray() :
m_aT(NULL), m_nSize(0), m_nAllocSize(0)
{
}
~CSimpleArray();
CSimpleArray(_In_ const CSimpleArray< T, TEqual >& src) :
m_aT(NULL), m_nSize(0), m_nAllocSize(0)
{
if (src.GetSize())
{
m_aT = (T*)calloc(src.GetSize(), sizeof(T));
if (m_aT != NULL)
{
m_nAllocSize = src.GetSize();
for (int i=0; i<src.GetSize(); i++)
Add(src[i]);
}
}
}
CSimpleArray< T, TEqual >& operator=(_In_ const CSimpleArray< T, TEqual >& src)
{
if (GetSize() != src.GetSize())
{
RemoveAll();
m_aT = (T*)calloc(src.GetSize(), sizeof(T));
if (m_aT != NULL)
m_nAllocSize = src.GetSize();
}
else
{
for (int i = GetSize(); i > 0; i--)
RemoveAt(i - 1);
}
for (int i=0; i<src.GetSize(); i++)
Add(src[i]);
return *this;
}
// Operations
int GetSize() const
{
return m_nSize;
}
BOOL Add(_In_ const T& t)
{
if(m_nSize == m_nAllocSize)
{
// Make sure newElement is not a reference to an element in the array.
// Or else, it will be invalidated by the reallocation.
ATLENSURE( (&t < m_aT) ||
(&t >= (m_aT + m_nAllocSize) ) );
T* aT;
int nNewAllocSize = (m_nAllocSize == 0) ? 1 : (m_nSize * 2);
if (nNewAllocSize<0||nNewAllocSize>INT_MAX/sizeof(T))
{
return FALSE;
}
aT = (T*)_recalloc(m_aT, nNewAllocSize, sizeof(T));
if(aT == NULL)
return FALSE;
m_nAllocSize = nNewAllocSize;
m_aT = aT;
}
InternalSetAtIndex(m_nSize, t);
m_nSize++;
return TRUE;
}
BOOL Remove(_In_ const T& t)
{
int nIndex = Find(t);
if(nIndex == -1)
return FALSE;
return RemoveAt(nIndex);
}
BOOL RemoveAt(_In_ int nIndex)
{
ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
if (nIndex < 0 || nIndex >= m_nSize)
return FALSE;
m_aT[nIndex].~T();
if(nIndex != (m_nSize - 1))
Checked::memmove_s((void*)(m_aT + nIndex), (m_nSize - nIndex) * sizeof(T), (void*)(m_aT + nIndex + 1), (m_nSize - (nIndex + 1)) * sizeof(T));
m_nSize--;
return TRUE;
}
void RemoveAll()
{
if(m_aT != NULL)
{
for(int i = 0; i < m_nSize; i++)
m_aT[i].~T();
free(m_aT);
m_aT = NULL;
}
m_nSize = 0;
m_nAllocSize = 0;
}
const T& operator[] (_In_ int nIndex) const
{
ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
if(nIndex < 0 || nIndex >= m_nSize)
{
_AtlRaiseException((DWORD)EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
}
return m_aT[nIndex];
}
T& operator[] (_In_ int nIndex)
{
ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
if(nIndex < 0 || nIndex >= m_nSize)
{
_AtlRaiseException((DWORD)EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
}
return m_aT[nIndex];
}
T* GetData() const
{
return m_aT;
}
int Find(_In_ const T& t) const
{
for(int i = 0; i < m_nSize; i++)
{
if(TEqual::IsEqual(m_aT[i], t))
return i;
}
return -1; // not found
}
BOOL SetAtIndex(
_In_ int nIndex,
_In_ const T& t)
{
if (nIndex < 0 || nIndex >= m_nSize)
return FALSE;
InternalSetAtIndex(nIndex, t);
return TRUE;
}
// Implementation
class Wrapper
{
public:
Wrapper(_In_ const T& _t) : t(_t)
{
}
template <class _Ty>
void * __cdecl operator new(
_In_ size_t,
_In_ _Ty* p)
{
return p;
}
template <class _Ty>
void __cdecl operator delete(
_In_ void* /* pv */,
_In_ _Ty* /* p */)
{
}
T t;
};
// Implementation
void InternalSetAtIndex(
_In_ int nIndex,
_In_ const T& t)
{
new(m_aT + nIndex) Wrapper(t);
}
typedef T _ArrayElementType;
T* m_aT;
int m_nSize;
int m_nAllocSize;
};
#define CSimpleValArray CSimpleArray
template <class T, class TEqual> inline CSimpleArray<T, TEqual>::~CSimpleArray()
{
RemoveAll();
}
以上就是我们所要说的CSimpleArray类 的主要代码。我之所以把这个类作为典范,是因为它足够简单,而且充分展现了模板类的魅力。你可以从它的代码里学习C++的模板类语法,同时你还可以学习模板成员变量以及模板函数的写法儿。实际上我经常在我的程序里使用这个类,如果是简单的结构或者简单类型(int,long,float,double,char,byte)的数组,我几乎第一个想到的就是这个集合类,它们工作的很好,从没有让我失望。
下面是一个示例:
struct TestStru
{
int m_order;
TCHAR m_name[25];
};
CSimpleArray<TestStru> testAry;
TestStru ts;
ts.m_order = 1;
lstrcpy( ts.m_name, _T("测试A"));
testAry.Add(ts);
ts.m_order = 2;
lstrcpy( ts.m_name, _T("测试B"));
testAry.Add(ts);
ts.m_order = 3;
lstrcpy( ts.m_name, _T("测试C"));
testAry.Add(ts);
你这么使用时没有问题的,这个TestStru换做其他类型,比如FLOAT, 工作的只会更好。CSimpleArray 类在我们已知数组大小的情况下效率有些欠缺。因为它缺少一个一次性初始化数组内存的函数。但如果你不能确定数组大小,那么它很合适,而且效率不低。
CSimpleArray类,你可以复制一个数组, 你还可以删除指定位置的数组项,或者用下标方式给数组的项赋新值。
CSimpleArray<TestStru> copyAry(testAry);
ts.m_order = 3;
lstrcpy( ts.m_name, _T("将被删除"));
testAry[2] = ts;
testAry.RemoveAt(2);
你还可以查找一个项,或者删除一个指定的项。当然这涉及到判断相等的操作,这需要一点前提。 对于int,float类型,编译器知道他们的相等操作,如果是你自己定义的结构,那么编译器是不知道的。
CSimpleArray 类好就好在,如果你没有用到这个操作, 前面讲的就够了,编译器也不会报错。如果你用到了相等操作,比如查找,比如删除。那么编译器会要求你为你的结构提供==操作符。
int nFind = testAry.Find(ts);
testAry.Remove(ts);
增加上面两行代码编译器会报错。提示没有 == 操作符,所以我们修改TestStruct 的代码,添加自定义操作符 == ,如下:
struct TestStru
{
int m_order;
TCHAR m_name[25];
bool operator ==(const struct TestStru &t) const
{
return (lstrcmp(this->m_name, t.m_name ) == 0);
}
};
这么一来,编译可以通过了。如果两个结构的m_name字段的文本相同,就认为他们相等。
到这里这个CSimpleArray类就介绍完了。需要补充一点的就是, CSimpleArray类 也可以处理指针,你可以把任何一个类的指针作为模板类, 建立数组。例如:
CSimpleArray<TestStru*> ptrArray; 这样用也没有问题,这里是指针的话,那么最后就涉及指针的申请和释放问题,处理好也没什么问题。
最后一点,额外啰嗦一下,下面这个包装类:
template <typename T>
class Wrapper
{
public:
Wrapper(_In_ const T& _t) : t(_t)
{
}
template <class _Ty>
void *operator new(
_In_ size_t,
_In_ _Ty* p)
{
return p;
}
template <class _Ty>
void operator delete(
_In_ void* /* pv */,
_In_ _Ty* /* p */)
{
}
T t;
};
如果没有任何解释就可以看懂这个类的作用的,那么你的C++水平应该不低了。这个类使用的非常巧妙。它实际上是解决T类型的赋值和构造函数调用问题。改写了new操作符,它只是返回了输入参数的指针位置,并没有真的申请内存, 编辑器看到这个返回指针,就会在这个基础上调用Wrapper的构造函数,于是完成了初始化。