• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 74, 文章 - 0, 評論 - 26, 引用 - 0
            數據加載中……

            在 C/C++ 中如何構造通用的對象鏈表

            一個簡化的問題示例

            鏈表的難點在于必須復制鏈表處理函數來處理不同的對象,即便邏輯是完全相同的。例如兩個結構類似的鏈表:


            struct Struct_Object_A
              {
                int a;
                int b;
                Struct_Object_A *next;
              
              } OBJECT_A;
              
              typedef struct Struct_Object_B
              {
                int a;
                int b;
                int c;
                Struct_Object_B *next;
              
              } OBJECT_B; 

            上面定義的兩個結構只有很小的一點差別。OBJECT_B 和 OBJECT_A 之間只差一個整型變量。但是,在編譯器看來,它們仍然是非常不同的。必須為存儲在鏈表中的每個對象復制用來添加、刪除和搜索鏈表的函數。為了解決這個問題,可以使用具有全部三個變量的一個聯合或結構,其中整數 c 并不是在所有的情況下都要使用。這可能變得非常復雜,并會形成不良的編程風格。

            C 代碼解決方案:虛擬鏈表

            此問題更好的解決方案之一是虛擬鏈表。虛擬鏈表是只包含鏈表指針的鏈表。對象存儲在鏈表結構背后。這一點是這樣實現的,首先為鏈表節點分配內存,接著為對象分配內存,然后將這塊內存分配給鏈表節點指針,如下所示:

            虛擬鏈表結構的一種實現

            typedef struct liststruct
              {
                liststruct *next;
              
              } LIST, *pLIST;
              
              pLIST Head = NULL;
              
              pLIST AddToList( pLIST Head,
            void * data, size_t datasize )
              {
              pLIST newlist=NULL;
              void *p;
              
                // 分配節點內存和數據內存
                newlist = (pLIST) malloc
            ( datasize + sizeof( LIST ) );
              
                // 為這塊數據緩沖區指定一個指針
                p = (void *)( newlist + 1 );
              
                // 復制數據
                memcpy( p, data, datasize );
              
                // 將這個節點指定給鏈表的表頭
                if( Head )
                {
                newlist->next = Head;
                }
                else
                newlist->next = NULL;
              
                Head = newlist;
              
                return Head;
              }  

            鏈表節點現在建立在數據值副本的基本之上。這個版本能很好地處理標量值,但不能處理帶有用 malloc 或 new 分配的元素的對象。要處理這些對象,LIST 結構需要包含一個一般的解除函數指針,這個指針可用來在將節點從鏈表中刪除并解除它之前釋放內存(或者關閉文件,或者調用關閉方法)。

            一個帶有解除函數的鏈表

            typedef void (*ListNodeDestructor)( void * );
              
              typedef struct liststruct
              {
                ListNodeDestructor DestructFunc;
                liststruct *next;
              
              } LIST, *pLIST;
              
              pLIST AddToList( pLIST Head, void * data, 
            size_t datasize,
              ListNodeDestructor Destructor )
              {
              pLIST newlist=NULL;
              void *p;
              
              
                // 分配節點內存和數據內存
                newlist = (pLIST) malloc
            ( datasize + sizeof( LIST ) );
              
                // 為這塊數據緩沖區指定一個指針
                p = (void *)( newlist + 1 );
              
                // 復制數據
                memcpy( p, data, datasize );
              
                newlist->DestructFunc = Destructor;
                
                // 將這個節點指定給鏈表的表頭
                if( Head )
                {
                  newlist->next = Head;
                }
                else
                  newlist->next = NULL;
              
                Head = newlist;
              
                return Head;
              }
              
              void DeleteList( pLIST Head )
              {
                pLIST Next;
                while( Head )
                {
                  Next = Head->next;
                  Head->DestructFunc( 
            (void *) Head );
                  free( Head );
                  Head = Next;
                }
              }
              
              typedef struct ListDataStruct
              {
                LPSTR p;
              
              } LIST_DATA, *pLIST_DATA;
              
              void ListDataDestructor( void *p )
              {
                // 對節點指針進行類型轉換
                pLIST pl = (pLIST)p;
              
                // 對數據指針進行類型轉換
                pLIST_DATA pLD = (pLIST_DATA)
            ( pl + 1 );
              
                delete pLD->p;
              }
              pLIST Head = NULL;
              
              void TestList()
              {
                pLIST_DATA d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "Hello" ); 
                Head = AddToList( Head, (void *) d,
            sizeof( pLIST_DATA ),
                ListDataDestructor );
                // 該對象已被復制,現在刪除原來的對象
                delete d;
              
                d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "World" ); 
                Head = AddToList( Head, (void *) d,
            sizeof( pLIST_DATA ),
                ListDataDestructor );
                delete d;
              
                // 釋放鏈表
                DeleteList( Head );
              }   

            在每個鏈表節點中包含同一個解除函數的同一個指針似乎是浪費內存空間。確實如此,但只有鏈表始終包含相同的對象才屬于這種情況。按這種方式編寫鏈表允許您將任何對象放在鏈表中的任何位置。大多數鏈表函數要求對象總是相同的類型或類。

            虛擬鏈表則無此要求。它所需要的只是將對象彼此區分開的一種方法。要實現這一點,您既可以檢測解除函數指針的值,也可以在鏈表中所用的全部結構前添加一個類型值并對它進行檢測。

            當然,如果要將鏈表編寫為一個 C++ 類,則對指向解除函數的指針的設置和存儲只能進行一次。

            C++ 解決方案:類鏈表

            本解決方案將 CList 類定義為從 LIST 結構導出的一個類,它通過存儲解除函數的單個值來處理單個存儲類型。請注意添加的 GetCurrentData() 函數,該函數完成從鏈表節點指針到數據偏移指針的數學轉換。一個虛擬鏈表對象

            // 定義解除函數指針
              
              typedef void (*ListNodeDestructor)
            ( void * );
              
              // 未添加解除函數指針的鏈表
              
              typedef struct ndliststruct
              {
                ndliststruct *next;
              
              } ND_LIST, *pND_LIST;
              
              // 定義處理一種數據類型的鏈表類
              
              class CList : public ND_LIST
              {
              public:
                CList(ListNodeDestructor);
                ~CList();
                pND_LIST AddToList
            ( void * data, size_t datasize );
                void *GetCurrentData();
                void DeleteList( pND_LIST Head );
              
              
              private:
                pND_LIST m_HeadOfList;
                pND_LIST m_CurrentNode;
                ListNodeDestructor
            m_DestructFunc;
              };
              
              // 用正確的起始值構造這個鏈表對象
              
              CList::CList(ListNodeDestructor Destructor)
                : m_HeadOfList(NULL), 
            m_CurrentNode(NULL)
              {
                m_DestructFunc = Destructor;
              }
              
              // 在解除對象以后刪除鏈表
              
              CList::~CList()
              {
                DeleteList(m_HeadOfList);
              }
              
              // 向鏈表中添加一個新節點
              
              pND_LIST CList::AddToList
            ( void * data, size_t datasize )
              {
              pND_LIST newlist=NULL;
              void *p;
              
              
                // 分配節點內存和數據內存
                newlist = (pND_LIST) malloc
            ( datasize + sizeof( ND_LIST ) );
              
                // 為這塊數據緩沖區指定一個指針
                p = (void *)( newlist + 1 );
              
                // 復制數據
                memcpy( p, data, datasize );
              
                // 將這個節點指定給鏈表的表頭
                if( m_HeadOfList )
                {
                  newlist->next = m_HeadOfList;
                }
                else
                  newlist->next = NULL;
              
                m_HeadOfList = newlist;
              
                return m_HeadOfList;
              }
              
              // 將當前的節點數據作為 void 類型返回,
            以便調用函數能夠將它轉換為任何類型
              
              void * CList::GetCurrentData()
              {
                return (void *)(m_CurrentNode+1);
              }
              
              // 刪除已分配的鏈表
              
              void CList::DeleteList( pND_LIST Head )
              {
                pND_LIST Next;
                while( Head )
                {
                  Next = Head->next;
                  m_DestructFunc( (void *) Head );
                  free( Head );
                  Head = Next;
                }
              }
              
              // 創建一個要在鏈表中創建和存儲的結構
              
              typedef struct ListDataStruct
              {
                LPSTR p;
              
              } LIST_DATA, *pND_LIST_DATA;
              
              // 定義標準解除函數
              
              void ClassListDataDestructor( void *p )
              {
                // 對節點指針進行類型轉換
                pND_LIST pl = (pND_LIST)p;
              
                // 對數據指針進行類型轉換
                pND_LIST_DATA pLD = (pND_LIST_DATA)
            ( pl + 1 );
              
                delete pLD->p;
              }
              
              // 測試上面的代碼
              
              void MyCListClassTest()
              {
                // 創建鏈表類
              
                CList* pA_List_of_Data =
            new CList(ClassListDataDestructor);
              
                // 創建數據對象
                
                pND_LIST_DATA d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "Hello" ); 
              
                // 創建指向鏈表頂部的局部指針
              
                pND_LIST Head = NULL;
              
                //向鏈表中添加一些數據
              
                Head = pA_List_of_Data->AddToList
            ( (void *) d, 
                sizeof( pND_LIST_DATA ) );
                // 該對象已被復制,現在刪除原來的對象
                delete d;
              
                // 確認它已被存儲
                char * p = ((pND_LIST_DATA) pA_List_of_Data->GetCurrentData())->p;
              
                d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "World" ); 
                Head = pA_List_of_Data->AddToList
            ( (void *) d, sizeof( pND_LIST_DATA ) );
                // 該對象已被復制,現在刪除原來的對象
                delete d;
              
                // 確認它已被存儲
                p = ((pND_LIST_DATA) 
            pA_List_of_Data->GetCurrentData())->p;
              
                // 刪除鏈表類,析構函數將刪除鏈表
                delete pA_List_of_Data;
              }
              

            小結

            從前面的討論來看,似乎僅編寫一個簡單的鏈表就要做大量的工作,但這只須進行一次。很容易將這段代碼擴充為一個處理排序、搜索以及各種其他任務的 C++ 類,并且這個類可以處理任何數據對象或類(在一個項目中,它處理大約二十個不同的對象)。您永遠不必重新編寫這段代碼。

            posted on 2006-06-26 19:37 井泉 閱讀(261) 評論(0)  編輯 收藏 引用 所屬分類: 數據結構

            国产精品永久久久久久久久久| 精品国产乱码久久久久久郑州公司| 国产午夜精品理论片久久影视 | 久久久久高潮综合影院| 亚洲午夜久久久久妓女影院| 久久久久亚洲AV片无码下载蜜桃| 99久久免费只有精品国产| 久久精品国产欧美日韩99热| 国产一级做a爰片久久毛片| 久久亚洲视频| 九九久久99综合一区二区| 伊人伊成久久人综合网777| 久久久精品免费国产四虎| 国产精品99久久久精品无码| 久久精品国产免费一区| 国产毛片欧美毛片久久久 | 久久精品国产色蜜蜜麻豆| 情人伊人久久综合亚洲| 亚洲国产精品18久久久久久| 精品久久久久久无码中文字幕| 久久WWW免费人成一看片| 久久综合色之久久综合| 亚洲国产精品人久久| 国产精品免费看久久久| 久久WWW免费人成一看片| 亚洲欧洲中文日韩久久AV乱码| 丁香久久婷婷国产午夜视频| 狠狠88综合久久久久综合网| 99精品国产免费久久久久久下载| 久久夜色精品国产亚洲av| 中文精品久久久久国产网址| 99久久777色| 久久精品一区二区国产| 久久婷婷五月综合97色| 精品伊人久久大线蕉色首页| 久久午夜综合久久| 久久最新免费视频| 久久无码AV中文出轨人妻| 精品久久久久久中文字幕大豆网| 久久久久se色偷偷亚洲精品av| 久久久无码精品亚洲日韩蜜臀浪潮|