昨天学习了通过建立一个CSimpleList类来实现一个数据链表,通过类中的接口函数实现对数据链表中的数据的增加和删除。但是我一直没有看明白CSimpleList::AddHead(void* p)和CSimpleList::Remove(void* p)这两个实现增加和删除的接口函数:
void CSimpleList::AddHead(void* p) { *GetNextPtr(p) = m_pHead; m_pHead = p; }
inline void** CSimpleList::GetNextPtr(void* p) const { return (void**)((BYTE*)p + m_nNextOffset);}
首先看看AddHead(void* p)这个接口函数,它的作用是把加进来的元素的地址传给m_pHead这个空指针变量,同时把原来的首元素的地址放在*GetNextPtr(p)中,这里大家应该意识到了,这个m_pHead是存放数据链表中首元素的地址的。
这里大家又要注意一下
void** CSimpleList::GetNextPtr(void* p)
这个函数的返回值是指向指针的指针类型的,m_nNextOffset这个是存放数据结构(就是我们的数据链表中的元素)中pNext成员的偏移量,这个是用来干什么的呢???
我们需要知道,在数据链表中,元素中除了有自己的数据外,还包含了下一个元素的地址,这样,我们知道其中一个元素,就知道下一个元素在哪,从而整个元素(数据)表中就像有条线联系着各个元素,彼此相互联系。
现在,关键是如何进行元素的增加和删除了:
(1)增加元素:我们增加的这个元素会放在链表首位,同时还要给它下一个(之前的首元素)元素的地址,这样就完成了增加操作。
(2)删除元素:删除元素有点特别,我们首先要判断这个待删除的元素是不是链表首元素,如果是首元素,则
m_pHead = *GetNextPtr(p); //p是要删除的元素地址,这里是把首元素中记录下一元素的地址传给m_pHead,这里有点拗口啊,要细心 //理解;
如果待删除的不是首元素,那么就要遍历整个数据链表中的元素,当我们查到某一元素的下一元素为要删除的元素时,那么就要把待删除元素中包含的下一元素地址传给当前这个元素中那个存放下一元素地址的成员;
BOOL CSimpleList::Remove(void* p) { if(p == NULL) return FALSE; BOOL bResult = FALSE; //假设移除失败 if(p == m_pHead) { //要移除头元素 m_pHead = *GetNextPtr(p); bResult = TRUE ; } else { //试图在表中查找要移除的元素 void* pTest = m_pHead; while(pTest != NULL && *GetNextPtr(pTest) != p) pTest = *GetNextPtr(pTest); //如果找到了,就将元素移除 if(pTest != NULL) { *GetNextPtr(pTest) = *GetNextPtr(p); bResult = TRUE; } } return bResult; }
好了,现在我们知道了数据链表中增加元素和删除元素的算法了,接下来就是实践一下,这里我们在主程序中要对两个很重要的CSimpleList类中的成员变量m_nNextOffset初始化为指定的值,什么意思呢,就是要把CSimpleList类的pNext成员的偏移量传给m_nNextOffset(我们用到类中的一个内联函数Constuct(int nNextOffset)来实现这个初始化)。
void main() { MyThreadData* pData; //CTypedSimpleList<MyThreadData*> list; CSimpleList list; list.Construct(offsetof(MyThreadData, pNext)); //告诉CSimpleList类pNext成员的偏移量 //向链表中添加成员 for( int i = 0;i<10;i++) { pData = new MyThreadData; pData->nSomeData = i; list.AddHead(pData); } //...... ........ //使用链表中的数据 //遍历整个链表,释放MyThreadData对象占用的空间 pData = (MyThreadData*)list.GetHead(); while(pData != NULL) { MyThreadData* pNextData = pData->pNext; printf( "The value of nSomeData is: %d \n",pData->nSomeData); delete pData; pData = pNextData; } }