(1)CList Class Members
Construction
CList | Constructs an empty ordered list. |
Head/Tail Access
GetHead | Returns the head element of the list (cannot be empty). |
GetTail | Returns the tail element of the list (cannot be empty). |
Operations
RemoveHead | Removes the element from the head of the list. |
RemoveTail | Removes the element from the tail of the list. |
AddHead | Adds an element (or all the elements in another list) to the head of the list (makes a new head). |
AddTail | Adds an element (or all the elements in another list) to the tail of the list (makes a new tail). |
RemoveAll | Removes all the elements from this list. |
Iteration
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. |
Retrieval/Modification
GetAt | Gets the element at a given position. |
SetAt | Sets the element at a given position. |
RemoveAt | Removes an element from this list, specified by position. |
Insertion
InsertBefore | Inserts a new element before a given position. |
InsertAfter | Inserts a new element after a given position. |
Searching
Find | Gets the position of an element specified by pointer value. |
FindIndex | Gets the position of an element specified by a zero-based index. |
Status
GetCount | Returns the number of elements in this list. |
IsEmpty | Tests for the empty list condition (no elements). |
(2)CList Class Members Using Demo
ConstructionCList<int,int> myList;//建立一个int链表
CList<CString,CString&> myList(16);//建立一个cstring的链表,后面的16表示链表里面数据的个数
CList<MyType,MyType&> myList;//建立一个MYTYPE类型(自定义)的链表,如果不存入数据的话,刚建立的链表是空的,头尾都为空
其它成员函数使用例子请点击该链接:成员函数使用例子
(3)源码
CPlexstruct CPlex // warning variable length structure
{
CPlex* pNext;
#ifndef _WIN64
#if (_AFX_PACKING >= 8)
DWORD dwReserved[1]; // align on 8 byte boundary
#endif
#endif
// BYTE data[maxNum*elementSize];
void* data() { return this+1; }
static CPlex* PASCAL Create(CPlex*& head, UINT_PTR nMax, UINT_PTR cbElement);
// like 'calloc' but no zero fill
// may throw memory exceptions
void FreeDataChain(); // free this one and links
};
/
// CPlex
CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT_PTR nMax, UINT_PTR cbElement)
{
ASSERT(nMax > 0 && cbElement > 0);
if (nMax == 0 || cbElement == 0)
{
AfxThrowInvalidArgException();
}
CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement];
// may throw exception
p->pNext = pHead;
pHead = p; // change head (adds in reverse order for simplicity)
return p;
}
void CPlex::FreeDataChain() // free this one and links
{
CPlex* p = this;
while (p != NULL)
{
BYTE* bytes = (BYTE*) p;
CPlex* pNext = p->pNext;
delete[] bytes;
p = pNext;
}
}
CList
template<class TYPE, class ARG_TYPE>
BOOL AFXAPI CompareElements(const TYPE* pElement1, const ARG_TYPE* pElement2)
{
ENSURE(pElement1 != NULL && pElement2 != NULL);
ASSERT(AfxIsValidAddress(pElement1, sizeof(TYPE), FALSE));
ASSERT(AfxIsValidAddress(pElement2, sizeof(ARG_TYPE), FALSE));
return *pElement1 == *pElement2;
}
/
// CList<TYPE, ARG_TYPE>
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;
INT_PTR GetSize() const;
BOOL IsEmpty() const;
// peek at head or tail
TYPE& GetHead();
const TYPE& GetHead() const;
TYPE& GetTail();
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;
POSITION GetTailPosition() const;
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
};
/
// CList<TYPE, ARG_TYPE> inline functions
template<class TYPE, class ARG_TYPE>
INT_PTR CList<TYPE, ARG_TYPE>::GetCount() const
{
return m_nCount;
}
template<class TYPE, class ARG_TYPE>
INT_PTR CList<TYPE, ARG_TYPE>::GetSize() const
{
return m_nCount;
}
template<class TYPE, class ARG_TYPE>
BOOL CList<TYPE, ARG_TYPE>::IsEmpty() const
{
return m_nCount == 0;
}
template<class TYPE, class ARG_TYPE>
TYPE& CList<TYPE, ARG_TYPE>::GetHead()
{
ENSURE(m_pNodeHead != NULL);
return m_pNodeHead->data;
}
template<class TYPE, class ARG_TYPE>
const TYPE& CList<TYPE, ARG_TYPE>::GetHead() const
{
ENSURE(m_pNodeHead != NULL);
return m_pNodeHead->data;
}
template<class TYPE, class ARG_TYPE>
TYPE& CList<TYPE, ARG_TYPE>::GetTail()
{
ENSURE(m_pNodeTail != NULL);
return m_pNodeTail->data;
}
template<class TYPE, class ARG_TYPE>
const TYPE& CList<TYPE, ARG_TYPE>::GetTail() const
{
ENSURE(m_pNodeTail != NULL);
return m_pNodeTail->data;
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::GetHeadPosition() const
{
return (POSITION) m_pNodeHead;
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::GetTailPosition() const
{
return (POSITION) m_pNodeTail;
}
template<class TYPE, class ARG_TYPE>
TYPE& CList<TYPE, ARG_TYPE>::GetNext(POSITION& rPosition) // return *Position++
{
CNode* pNode = (CNode*) rPosition;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
rPosition = (POSITION) pNode->pNext;
return pNode->data;
}
template<class TYPE, class ARG_TYPE>
const TYPE& CList<TYPE, ARG_TYPE>::GetNext(POSITION& rPosition) const // return *Position++
{
CNode* pNode = (CNode*) rPosition;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
rPosition = (POSITION) pNode->pNext;
return pNode->data;
}
template<class TYPE, class ARG_TYPE>
TYPE& CList<TYPE, ARG_TYPE>::GetPrev(POSITION& rPosition) // return *Position--
{
CNode* pNode = (CNode*) rPosition;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
rPosition = (POSITION) pNode->pPrev;
return pNode->data;
}
template<class TYPE, class ARG_TYPE>
const TYPE& CList<TYPE, ARG_TYPE>::GetPrev(POSITION& rPosition) const // return *Position--
{
CNode* pNode = (CNode*) rPosition;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
rPosition = (POSITION) pNode->pPrev;
return pNode->data;
}
template<class TYPE, class ARG_TYPE>
TYPE& CList<TYPE, ARG_TYPE>::GetAt(POSITION position)
{
CNode* pNode = (CNode*) position;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
return pNode->data;
}
template<class TYPE, class ARG_TYPE>
AFX_INLINE const TYPE& CList<TYPE, ARG_TYPE>::GetAt(POSITION position) const
{
CNode* pNode = (CNode*) position;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
return pNode->data;
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::SetAt(POSITION pos, ARG_TYPE newElement)
{
CNode* pNode = (CNode*) pos;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
pNode->data = newElement;
}
template<class TYPE, class ARG_TYPE>
CList<TYPE, ARG_TYPE>::CList(INT_PTR nBlockSize)
{
ASSERT(nBlockSize > 0);
m_nCount = 0;
m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
m_pBlocks = NULL;
m_nBlockSize = nBlockSize;
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::RemoveAll()
{
ASSERT_VALID(this);
// 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;
}
template<class TYPE, class ARG_TYPE>
CList<TYPE, ARG_TYPE>::~CList()
{
RemoveAll();
ASSERT(m_nCount == 0);
}
/
// Node helpers
//
// Implementation note: CNode's are stored in CPlex blocks and
// chained together. Free blocks are maintained in a singly linked list
// using the 'pNext' member of CNode with 'm_pNodeFree' as the head.
// Used blocks are maintained in a doubly linked list using both 'pNext'
// and 'pPrev' as links and 'm_pNodeHead' and 'm_pNodeTail'
// as the head/tail.
//
// We never free a CPlex block unless the List is destroyed or RemoveAll()
// is used - so the total number of CPlex blocks may grow large depending
// on the maximum past size of the list.
//
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;
}
}
ENSURE(m_pNodeFree != NULL); // we must have something
CList::CNode* pNode = m_pNodeFree;
m_pNodeFree = m_pNodeFree->pNext;
pNode->pPrev = pPrev;
pNode->pNext = pNext;
m_nCount++;
ASSERT(m_nCount > 0); // make sure we don't overflow
#pragma push_macro("new")
#undef new
::new( (void*)( &pNode->data ) ) TYPE;
#pragma pop_macro("new")
return pNode;
}
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--;
ASSERT(m_nCount >= 0); // make sure we don't underflow
// if no more elements, cleanup completely
if (m_nCount == 0)
RemoveAll();
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::AddHead(ARG_TYPE newElement)
{
ASSERT_VALID(this);
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>
POSITION CList<TYPE, ARG_TYPE>::AddTail(ARG_TYPE newElement)
{
ASSERT_VALID(this);
CNode* pNewNode = NewNode(m_pNodeTail, NULL);
pNewNode->data = newElement;
if (m_pNodeTail != NULL)
m_pNodeTail->pNext = pNewNode;
else
m_pNodeHead = pNewNode;
m_pNodeTail = pNewNode;
return (POSITION) pNewNode;
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::AddHead(CList* pNewList)
{
ASSERT_VALID(this);
ENSURE(pNewList != NULL);
ASSERT_VALID(pNewList);
// add a list of same elements to head (maintain order)
POSITION pos = pNewList->GetTailPosition();
while (pos != NULL)
AddHead(pNewList->GetPrev(pos));
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::AddTail(CList* pNewList)
{
ASSERT_VALID(this);
ENSURE(pNewList != NULL);
ASSERT_VALID(pNewList);
// add a list of same elements
POSITION pos = pNewList->GetHeadPosition();
while (pos != NULL)
AddTail(pNewList->GetNext(pos));
}
template<class TYPE, class ARG_TYPE>
TYPE CList<TYPE, ARG_TYPE>::RemoveHead()
{
ASSERT_VALID(this);
ENSURE(m_pNodeHead != NULL); // don't call on empty list !!!
ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode)));
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>
TYPE CList<TYPE, ARG_TYPE>::RemoveTail()
{
ASSERT_VALID(this);
ENSURE(m_pNodeTail != NULL); // don't call on empty list !!!
ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode)));
CNode* pOldNode = m_pNodeTail;
TYPE returnValue = pOldNode->data;
m_pNodeTail = pOldNode->pPrev;
if (m_pNodeTail != NULL)
m_pNodeTail->pNext = NULL;
else
m_pNodeHead = NULL;
FreeNode(pOldNode);
return returnValue;
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::InsertBefore(POSITION position, ARG_TYPE newElement)
{
ASSERT_VALID(this);
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)
{
ASSERT(AfxIsValidAddress(pOldNode->pPrev, sizeof(CNode)));
pOldNode->pPrev->pNext = pNewNode;
}
else
{
ASSERT(pOldNode == m_pNodeHead);
m_pNodeHead = pNewNode;
}
pOldNode->pPrev = pNewNode;
return (POSITION) pNewNode;
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::InsertAfter(POSITION position, ARG_TYPE newElement)
{
ASSERT_VALID(this);
if (position == NULL)
return AddTail(newElement); // insert after nothing -> tail of the list
// Insert it before position
CNode* pOldNode = (CNode*) position;
ASSERT(AfxIsValidAddress(pOldNode, sizeof(CNode)));
CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext);
pNewNode->data = newElement;
if (pOldNode->pNext != NULL)
{
ASSERT(AfxIsValidAddress(pOldNode->pNext, sizeof(CNode)));
pOldNode->pNext->pPrev = pNewNode;
}
else
{
ASSERT(pOldNode == m_pNodeTail);
m_pNodeTail = pNewNode;
}
pOldNode->pNext = pNewNode;
return (POSITION) pNewNode;
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::RemoveAt(POSITION position)
{
ASSERT_VALID(this);
CNode* pOldNode = (CNode*) position;
ASSERT(AfxIsValidAddress(pOldNode, sizeof(CNode)));
// remove pOldNode from list
if (pOldNode == m_pNodeHead)
{
m_pNodeHead = pOldNode->pNext;
}
else
{
ASSERT(AfxIsValidAddress(pOldNode->pPrev, sizeof(CNode)));
pOldNode->pPrev->pNext = pOldNode->pNext;
}
if (pOldNode == m_pNodeTail)
{
m_pNodeTail = pOldNode->pPrev;
}
else
{
ASSERT(AfxIsValidAddress(pOldNode->pNext, sizeof(CNode)));
pOldNode->pNext->pPrev = pOldNode->pPrev;
}
FreeNode(pOldNode);
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::FindIndex(INT_PTR nIndex) const
{
ASSERT_VALID(this);
if (nIndex >= m_nCount || nIndex < 0)
return NULL; // went too far
CNode* pNode = m_pNodeHead;
while (nIndex--)
{
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
pNode = pNode->pNext;
}
return (POSITION) pNode;
}
template<class TYPE, class ARG_TYPE>
POSITION CList<TYPE, ARG_TYPE>::Find(ARG_TYPE searchValue, POSITION startAfter) const
{
ASSERT_VALID(this);
CNode* pNode = (CNode*) startAfter;
if (pNode == NULL)
{
pNode = m_pNodeHead; // start at head
}
else
{
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
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;
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::Serialize(CArchive& ar)
{
ASSERT_VALID(this);
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar.WriteCount(m_nCount);
for (CNode* pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext)
{
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
TYPE* pData;
/*
* in some cases the & operator might be overloaded, and we cannot use it to obtain
* the address of a given object. We then use the following trick to get the address
*/
pData = reinterpret_cast< TYPE* >( &reinterpret_cast< int& >( static_cast< TYPE& >( pNode->data ) ) );
SerializeElements<TYPE>(ar, pData, 1);
}
}
else
{
DWORD_PTR nNewCount = ar.ReadCount();
while (nNewCount--)
{
TYPE newData[1];
SerializeElements<TYPE>(ar, newData, 1);
AddTail(newData[0]);
}
}
}
#ifdef _DEBUG
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "with " << m_nCount << " elements";
if (dc.GetDepth() > 0)
{
POSITION pos = GetHeadPosition();
while (pos != NULL)
{
TYPE temp[1];
temp[0] = ((CList*)this)->GetNext(pos);
dc << "\n";
DumpElements<TYPE>(dc, temp, 1);
}
}
dc << "\n";
}
template<class TYPE, class ARG_TYPE>
void CList<TYPE, ARG_TYPE>::AssertValid() const
{
CObject::AssertValid();
if (m_nCount == 0)
{
// empty list
ASSERT(m_pNodeHead == NULL);
ASSERT(m_pNodeTail == NULL);
}
else
{
// non-empty list
ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode)));
ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode)));
}
}
#endif //_DEBUG
参考链接: CList成员函数