zoukankan      html  css  js  c++  java
  • Windows 程序设计学习记录(1)类的定义和应用

          最近开始学习Windows 程序设计,一边看那本王艳平写的书,一边练习编程,打算记录一下自己的学习过程和心得体会!!!!!!!!!

          今天学习了数据链表的建立,通过定义一个CSimpleList类来存储多个数据,通过建立的数据链表来实现对各个数据的添加,删除以及遍历表中的元素.遇到很多问题,很多难懂的地方,但不怕,慢慢来,一个一个解决.

           1、这个CSimpleList类是定义在一个_AFXTLS_.H头文件中,为了避免重复使用一组预编译指令:

    View Code
    #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是常函数的意思,表示这个成员函数不会改变类中的成员变量,只可读类中的成员,不可写。

          这个函数返回的是指针所在的地址,这个指针是什么呢?? 其实这个是一个结构体指针加上这个结构体里的一个成员的偏移量(有点难懂,想象一下这个结构体相当于一个数

    组,这个结构体指针是数组的首地址,那么结构体里的多个成员存储在内存中,每个成员就相对于这个首地址有了一个偏移量)说白了,这个就是结构体里的那个成员的地址。    

    View Code
    #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成员的地址作为链表中的第一个元素的地址就

    是移除头元素了。

    View Code
    #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文件中

    View Code
    #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;
        }
    }
  • 相关阅读:
    leetcode【dynamic】【0】
    VIM 常用指令
    《Java多线程编程核心技术》笔记
    log4j配置文件详解
    CSU 1803 2016(同余公式)2016年湖南省第十二届大学生计算机程序设计竞赛
    NYOJ 1233 差值(字符串排序+大数减法)
    HDU 5723 Abandoned country(最小生成树+DFS)
    POJ 1451 T9 字典树
    POJ 2965 The Pilots Brothers' refrigerator 状态压缩+广搜
    POJ 1753 Flip game状态压缩+广搜
  • 原文地址:https://www.cnblogs.com/YungMing/p/Ming.html
Copyright © 2011-2022 走看看