CList是一个双向链表类。
1:Clist类定义在Afxtempl.h 头文件中,因此在使用该类时,需要加这个头文件名。
2、理解CList的声明和构造方法
CList的声明如下:
template< class TYPE, class ARG_TYPE >class CList : public CObject
CList<CPoint, CPoint&> list;
这样就指定了CList中存放的是CPoint类型的引用。
由此,我们知道CList是一个模版类,那么他的两个class是什么意思呢?
下面看一个例子:
-
CList<CString ,CString&> list;//链表对象1
-
CList<CString,CString> list2;//链表对象2
这里的第一个参数CString是实例化的类型,第二个参数是类的成员函数的参数的调用形式,通常是类型 引用,当然也可以是对象,而不是引用。对象和引用的区别,可以看一下C++基础知识方面的书。
3、使用CList的迭代器
迭代器是近年在链表使用中的一个常用技术。如果大家学过java,就会知道,在java中,会有专门的一个迭代器类,这个迭代器类用来访问Collect中的数据元素。在mfc中,在CList中,定义了几个迭代器的函数。他们方便了我们去访问数据元素。
GetHeadPosition | Returns the position of the head element of the list. |
GetTailPosition | Returns the position of the tail element of the list. |
GetNext | Gets the next element for iterating. |
GetPrev | Gets the previous element for iterating. |
以上是四个迭代器函数。前两个他们的返回值是POSITION,它是mfc中定义的的一种专门访问Collect类的一个数据类型。用他来表示元素的位置。
-
// abstract iteration position
-
struct __POSITION { };
-
typedef __POSITION* POSITION;
以上是在跟踪POSITION定义中找到的。由此,我们知道POSITION是个抽象的迭带器位置。至于怎么实现,我也只能知道这么多,如果有哪位高手知道的话,一定要告诉我们哦。
下面我们利用给出的迭代器函数,对下面建立的链表进行遍历:
CList<CString ,CString&> list;
CString str1="hello";
CString str2="world";
CString str3="my";
CString str4="life";
list.AddTail(str1);
list.AddTail(str2);
list.AddTail(str3);
list.AddTail(str4);
POSITION pos = list.GetHeadPosition();
while(pos!=NULL)
{
pDC->TextOut(200,200,GetNext( POSITION& rPosition ) );//假设代码是在View类中的OnDraw()
//函数里
}
这样就完成了对list的遍历。
4、CList的其它函数:
CList的其它函数,大家可以参考一下MSDN,在那里每个函数都讲得很清楚。这里就不再多介绍。
5、CList的元素
对于系统定义的类,可以直接在CList中使用。而对于用户自定义的类,如果这个类继承了CObject类,在使用CList时,需要将用户自定义的类实现操作符=的重载,否则系统将会报错。以下举一简单的CNode例子,使之实现操作符=的重载:
class CNode : public CObject
{
public:
CPoint point;
CNode()
{
point.x=0;
point.y=0;
}
void operator = (CPoint& p)//运算符=的重载
{
point=p;
}
};
6、CList的序列化:
CList是一个继承了CObject的类,并且已经实现了Serialize(CArchive& ar)的函数,所以,对CList进行串行序列化不需要的。
如果在CList类在CDocument定义了其对象list,那么在文档的Serialize(CArchive& ar)只需作如下修改:
-
void CDdfDoc::Serialize(CArchive& ar)
-
{
-
if (ar.IsStoring())
-
{
-
// TODO: add storing code here
-
list.Serialize(ar);
-
}
-
else
-
{
-
// TODO: add loading code here
-
list.Serialize(ar);
-
}
-
}
这样,就实现了文档的串行化,如果使用mfc生成的框架,那么我们就可以直接将这个list的数据保存到文件,并能读取了。
CList声明的原型
-
template<class TYPE, class ARG_TYPE = const TYPE&> class CList : public CObject { protected: struct CNode { CNode* pNext; // 后指针 CNode* pPrev; // 前指针 TYPE data; // 数据 }; public: // Construction /* explicit */ CList(INT_PTR nBlockSize = 10); // Attributes (head and tail) // count of elements INT_PTR GetCount() const; // return m_nCount; INT_PTR GetSize() const; // return m_nCount; BOOL IsEmpty() const; // return m_nCount == 0; // peek at head or tail TYPE& GetHead(); // return m_pNodeHead->data; const TYPE& GetHead() const; TYPE& GetTail(); // return m_pNodeTail->data; const TYPE& GetTail() const; // Operations // get head or tail (and remove it) - don't call on empty list ! TYPE RemoveHead(); // 删除头部元素,并返回其元素值 TYPE RemoveTail(); // 删除尾部元素,并返回其元素值 // add before head or after tail POSITION AddHead(ARG_TYPE newElement); POSITION AddTail(ARG_TYPE newElement); // add another list of elements before head or after tail void AddHead(CList* pNewList); void AddTail(CList* pNewList); // remove all elements void RemoveAll(); // iteration POSITION GetHeadPosition() const; // return (POSITION) m_pNodeHead; POSITION GetTailPosition() const; // return (POSITION) m_pNodeTail; TYPE& GetNext(POSITION& rPosition); // return *Position++ const TYPE& GetNext(POSITION& rPosition) const; // return *Position++ TYPE& GetPrev(POSITION& rPosition); // return *Position-- const TYPE& GetPrev(POSITION& rPosition) const; // return *Position-- // getting/modifying an element at a given position TYPE& GetAt(POSITION position); const TYPE& GetAt(POSITION position) const; void SetAt(POSITION pos, ARG_TYPE newElement); void RemoveAt(POSITION position); // inserting before or after a given position POSITION InsertBefore(POSITION position, ARG_TYPE newElement); POSITION InsertAfter(POSITION position, ARG_TYPE newElement); // helper functions (note: O(n) speed) POSITION Find(ARG_TYPE searchValue, POSITION startAfter = NULL) const; // defaults to starting at the HEAD, return NULL if not found POSITION FindIndex(INT_PTR nIndex) const; // get the 'nIndex'th element (may return NULL) // Implementation protected: CNode* m_pNodeHead; // 指向链表头的指针 CNode* m_pNodeTail; // 指向链表尾的指针 INT_PTR m_nCount; // 元素个数 CNode* m_pNodeFree; // 空闲节点链表 struct CPlex* m_pBlocks; // 数据块载体链表 INT_PTR m_nBlockSize; // 每个数据块元素个数 CNode* NewNode(CNode*, CNode*); // 创建新节点 void FreeNode(CNode*); // 释放节点数据 public: ~CList(); void Serialize(CArchive&); #ifdef _DEBUG void Dump(CDumpContext&) const; void AssertValid() const; #endif };
RemoveHead 和 FreeNode 函数
-
// 删除头部元素,并返回其元素值 template<class TYPE, class ARG_TYPE> TYPE CList<TYPE, ARG_TYPE>::RemoveHead() { CNode* pOldNode = m_pNodeHead; TYPE returnValue = pOldNode->data; m_pNodeHead = pOldNode->pNext; if (m_pNodeHead != NULL) m_pNodeHead->pPrev = NULL; else m_pNodeTail = NULL; FreeNode(pOldNode); return returnValue; } // 释放节点 template<class TYPE, class ARG_TYPE> void CList<TYPE, ARG_TYPE>::FreeNode(CNode* pNode) { pNode->data.~TYPE(); pNode->pNext = m_pNodeFree; // 将节点加入空闲节点链表 m_pNodeFree = pNode; m_nCount--; // if no more elements, cleanup completely if (m_nCount == 0) RemoveAll(); }
AddHead(插入元素) 和 NewNode 函数
-
// 在链表头部插入值为newElement的元素 template<class TYPE, class ARG_TYPE> POSITION CList<TYPE, ARG_TYPE>::AddHead(ARG_TYPE newElement) { CNode* pNewNode = NewNode(NULL, m_pNodeHead); pNewNode->data = newElement; if (m_pNodeHead != NULL) m_pNodeHead->pPrev = pNewNode; else m_pNodeTail = pNewNode; m_pNodeHead = pNewNode; return (POSITION) pNewNode; } // 创建一个新节点 template<class TYPE, class ARG_TYPE> typename CList<TYPE, ARG_TYPE>::CNode* CList<TYPE, ARG_TYPE>::NewNode(CNode* pPrev, CNode* pNext) { if (m_pNodeFree == NULL) { // add another block CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CNode)); // chain them into free list CNode* pNode = (CNode*) pNewBlock->data(); // free in reverse order to make it easier to debug pNode += m_nBlockSize - 1; for (INT_PTR i = m_nBlockSize-1; i >= 0; i--, pNode--) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } } CList::CNode* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; pNode->pPrev = pPrev; pNode->pNext = pNext; m_nCount++; ::new( (void*)( &pNode->data ) ) TYPE; return pNode; }
AddHead函数(插入链表)
-
/
/ 在链表头部插入pNewList所指链表 template<class TYPE, class ARG_TYPE> void CList<TYPE, ARG_TYPE>::AddHead(CList* pNewList) { // add a list of same elements to head (maintain order) POSITION pos = pNewList->GetTailPosition(); while (pos != NULL) AddHead(pNewList->GetPrev(pos)); }
RemoveAll函数
-
// 移除所有的元素 template<class TYPE, class ARG_TYPE> void CList<TYPE, ARG_TYPE>::RemoveAll() { // destroy elements CNode* pNode; for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext) pNode->data.~TYPE(); m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks->FreeDataChain(); // 释放数据链 m_pBlocks = NULL; }
RemoveAt函数
-
// 移除指定位置的元素 template<class TYPE, class ARG_TYPE> void CList<TYPE, ARG_TYPE>::RemoveAt(POSITION position) { CNode* pOldNode = (CNode*) position; // remove pOldNode from list if (pOldNode == m_pNodeHead) m_pNodeHead = pOldNode->pNext; else pOldNode->pPrev->pNext = pOldNode->pNext; if (pOldNode == m_pNodeTail) m_pNodeTail = pOldNode->pPrev; else pOldNode->pNext->pPrev = pOldNode->pPrev; FreeNode(pOldNode); // 释放节点数据。 }
InsertBefore函数
-
// 在指定位置之前插入值为newElement的元素。 template<class TYPE, class ARG_TYPE> POSITION CList<TYPE, ARG_TYPE>::InsertBefore(POSITION position, ARG_TYPE newElement) { if (position == NULL) return AddHead(newElement); // insert before nothing -> head of the list // Insert it before position CNode* pOldNode = (CNode*) position; CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode); pNewNode->data = newElement; if (pOldNode->pPrev != NULL) pOldNode->pPrev->pNext = pNewNode; else m_pNodeHead = pNewNode; pOldNode->pPrev = pNewNode; return (POSITION) pNewNode; }
Find函数
-
// 从startAfter后开始查找值为searchVaule元素,返回位置 template<class TYPE, class ARG_TYPE> POSITION CList<TYPE, ARG_TYPE>::Find(ARG_TYPE searchValue, POSITION startAfter) const { CNode* pNode = (CNode*) startAfter; if (pNode == NULL) pNode = m_pNodeHead; // start at head else pNode = pNode->pNext; // start after the one specified for (; pNode != NULL; pNode = pNode->pNext) if (CompareElements<TYPE>(&pNode->data, &searchValue)) return (POSITION)pNode; return NULL; }