Windows 程序设计学习记录(1)---类的定义和应用
最近开始学习Windows 程序设计,一边看那本王艳平写的书,一边练习编程,打算记录一下自己的学习过程和心得体会!!!!!!!!!
今天学习了数据链表的建立,通过定义一个CSimpleList类来存储多个数据,通过建立的数据链表来实现对各个数据的添加,删除以及遍历表中的元素.遇到很多问题,很多难懂的地方,但不怕,慢慢来,一个一个解决.
1、这个CSimpleList类是定义在一个_AFXTLS_.H头文件中,为了避免重复使用一组预编译指令:
#ifndef __AFXTLS_H__ //_AFXTLS_.H 文件 #define __AFXTLS_H__ /*********************/ #endif
下面是头文件_AFXTLS_.H中的内容。首先什么是类的内联函数??inline是啥啊?而且还写在类定义的外面,百度了一下,原来inline是代替C中表达式形式的宏定义,不但继
承了它没有参数压栈,没有代码生成等普通函数使用时需要的操作,而且还享受到C++编译器严格类型检查的好处,从而节省了很多时间,效率很高。具体的知识可以自己去找下。
类定义中有个内联函数是
inline void** CSimpleList::GetNextPtr(void* p) const
{ return (void**)((BYTE*)p + m_nNextOffset);}
void**表示GetNextPtr这个成员函数是指向指针的指针类型的,后面的const是常函数的意思,表示这个成员函数不会改变类中的成员变量,只可读类中的成员,不可写。
这个函数返回的是指针所在的地址,这个指针是什么呢?? 其实这个是一个结构体指针加上这个结构体里的一个成员的偏移量(有点难懂,想象一下这个结构体相当于一个数
组,这个结构体指针是数组的首地址,那么结构体里的多个成员存储在内存中,每个成员就相对于这个首地址有了一个偏移量)说白了,这个就是结构体里的那个成员的地址。
#ifndef __AFXTLS_H__ //_AFXTLS_.H 文件 #define __AFXTLS_H__ #include <Windows.h> #include <stddef.h> class CNoTrackObject; ////////////////////////////////// ///// CSimpleList /////////////// class CSimpleList { public: CSimpleList( int nNextOffset =0); void Construct( int nNextOffset); //提供给用户的接口函数 (Operations) , 用于添加\删除和遍历节点 BOOL IsEmpty() const; void AddHead(void* p); void RemoveAll(); void* GetHead() const; //如果函数的参数可以是任意类型指针,那么应声明其参数为void * void* GetNext(void* p) const; BOOL Remove(void* p); //为实现接口函数所需要的成员 ( Implementation) void* m_pHead; //链接中第一个元素的地址 size_t m_nNextOffset; //数据结构中pNext成员的偏移量,size_t 是无符号整数类型, //m_nNextOffset代表的是对象首地址到对象成员地址之间的偏移量 void** GetNextPtr( void* p) const; // 指向指针的指针 }; //类的内联函数???????????????????????????? inline CSimpleList::CSimpleList( int nNextOffset) { m_pHead = NULL;m_nNextOffset = nNextOffset;} inline void CSimpleList::Construct(int nNextOffset) { m_nNextOffset = nNextOffset;} inline BOOL CSimpleList::IsEmpty() const { return m_pHead == NULL;} inline void CSimpleList::RemoveAll() { m_pHead = NULL;} inline void* CSimpleList::GetHead() const { return m_pHead;} inline void* CSimpleList::GetNext( void* preElement) const { return *GetNextPtr(preElement);} inline void** CSimpleList::GetNextPtr(void* p) const { return (void**)((BYTE*)p + m_nNextOffset);}
好的,再来看看在AFXTLS.CPP里面的内容,很奇怪,为什么CSimpleList::AddHead(void* p)和CSimpleList::Remove(void* p)这两个个接口函数定义在
AFXTLS.CPP中呢?为什么不直接在头文件中定义好呢?当然又是百度一下,http://bbs.chinaunix.net/archiver/?tid-1610751.html,这个是讲解得最清楚的,非常好,都是牛人啊!
为了加深我的理解,我还是概括一下吧,头文件一般是进行声明用的,而.cpp文件一般是代码的实现,为什么要这样规定呢?因为如果头文件想包含谁就包含谁,一来头文件
很大,预编译慢,二来这头文件不敢改了,如果改了,包含了这个头文件的文件也会改,弄得很乱。
CSimpleList::AddHead(void* p)这个函数用于向链表中添加一个元素,并把新添加的元素放在表头,我们通过CSimpleList类中的m_pHead成员来标识第一个元素的地址
*GetNextPtr(p) = m_pHead;
m_pHead = p;
相当于: void*t;
t=GetNextPtr(p) ; //t是指向结构体P中那个成员的地址的指针
*t= m_pHead; //指针 t 指向链表中第一个元素的地址
m_pHead = p; //链表中一个元素的地址为传入的结构体指针
还有个疑问,在CSimpleList::Remove(void* p)中,为什么移除头元素是下面的代码:
m_pHead = *GetNextPtr(p); //把结构体中pNext成员的地址作为链表中第一个元素的地址,这里要明白链表的意思。可以这样理解,链表中的
元素既存有自己的数据,又存有下一个元素的地址,pNext成员就是存下一个元素的地址的,现在明白为什么把结构体中的pNext成员的地址作为链表中的第一个元素的地址就
是移除头元素了。
#include "_AFXTLS_.h" //-------------------------------------- void CSimpleList::AddHead(void* p) { *GetNextPtr(p) = m_pHead; m_pHead = p; } 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; }
下面是这个类的一个实例代码,在MyTls.cpp文件中
#include "_AFXTLS_.h" #include <process.h> #include <iostream> using namespace std; struct MyThreadData { MyThreadData* pNext; int nSomeData; }; 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; } }