在 C C 中如何構造通用的物件鍊錶

2021-08-22 09:47:32 字數 3275 閱讀 7219

t. w. burger ([email protected]), 老闆, thomas wolfgang burger consulting 公司

2000 年 9 月 01 日

您是否做過這樣乙個專案,它要求您在記憶體中儲存數目不定的若干不同物件?對於某些情況,二叉樹是最佳選擇,但在通常情況下,更簡單的鍊錶是顯而易見的選擇。

乙個簡化的問題示例

鍊錶的難點在於必須複製煉表處理函式來處理不同的物件,即便邏輯是完全相同的。例如:

兩個結構類似的鍊錶

struct struct_object_a

object_a;

typedef struct struct_object_b

object_b;

上面定義的兩個結構只有很小的一點差別。object_b 和 object_a 之間只差乙個整型變數。但是,在編譯器看來,它們仍然是非常不同的。必須為儲存在鍊錶中的每個物件複製用來新增、刪除和搜尋鍊錶的函式。為了解決這個問題,可以使用具有全部三個變數的乙個聯合或結構,其中整數 c 並不是在所有的情況下都要使用。這可能變得非常複雜,並會形成不良的程式設計風格。

c **解決方案:虛擬鍊錶

此問題更好的解決方案之一是虛擬鍊錶。虛擬鍊錶是只包含鍊錶指標的鍊錶。物件儲存在鍊錶結構背後。這一點是這樣實現的,首先為鍊錶節點分配記憶體,接著為物件分配記憶體,然後將這塊記憶體分配給鍊錶節點指標,如下所示:

虛擬鍊錶結構的一種實現

typedef struct liststruct

list, *plist;

plist head = null;

plist addtolist( plist head, void * data, size_t datasize )

else

newlist->next = null;

head = newlist;

return head;

}

鍊錶節點現在建立在資料值副本的基本之上。這個版本能很好地處理標量值,但不能處理帶有用 malloc 或 new 分配的元素的物件。要處理這些物件,list 結構需要包含乙個一般的解除函式指標,這個指標可用來在將節點從鍊錶中刪除並解除它之前釋放記憶體(或者關閉檔案,或者呼叫關閉方法)。

乙個帶有解除函式的鍊錶

typedef void (*listnodedestructor)( void * );

typedef struct liststruct

list, *plist;

plist addtolist( plist head, void * data, size_t datasize,

listnodedestructor destructor )

else

newlist->next = null;

head = newlist;

return head;

}void deletelist( plist head )

}typedef struct listdatastruct

list_data, *plist_data;

void listdatadestructor( void *p )

plist head = null;

void testlist()

在每個鍊錶節點中包含同乙個解除函式的同乙個指標似乎是浪費記憶體空間。確實如此,但只有鍊錶始終包含相同的物件才屬於這種情況。按這種方式編寫鍊錶允許您 將任何物件放在鍊錶中的任何位置。大多數鍊錶函式要求物件總是相同的型別或類。虛擬鍊錶則無此要求。它所需要的只是將物件彼此區分開的一種方法。要實現這 一點,您既可以檢測解除函式指標的值,也可以在鍊錶中所用的全部結構前新增乙個型別值並對它進行檢測。當然,如果要將鍊錶編寫為乙個 c++ 類,則對指向解除函式的指標的設定和儲存只能進行一次。

c++ 解決方案:類鍊錶

本解決方案將 clist 類定義為從 list 結構匯出的乙個類,它通過儲存解除函式的單個值來處理單個儲存型別。請注意新增的 getcurrentdata() 函式,該函式完成從鍊錶節點指標到資料偏移指標的數學轉換。

乙個虛擬鍊錶物件

// 定**除函式指標

typedef void (*listnodedestructor)( void * );

// 未新增解除函式指標的鍊錶

typedef struct ndliststruct

nd_list, *pnd_list;

// 定義處理一種資料型別的鍊錶類

class clist : public nd_list

;// 用正確的起始值構造這個鍊錶物件

clist::clist(listnodedestructor destructor)

: m_headoflist(null), m_currentnode(null)

// 在解除物件以後刪除鍊錶

clist::~clist()

// 向鍊錶中新增乙個新節點

pnd_list clist::addtolist( void * data, size_t datasize )

else

newlist->next = null;

m_headoflist = newlist;

return m_headoflist;

}// 將當前的節點資料作為 void 型別返回,以便呼叫函式能夠將它轉換為任何型別

void * clist::getcurrentdata()

// 刪除已分配的鍊錶

void clist::deletelist( pnd_list head )

}// 建立乙個要在鍊錶中建立和儲存的結構

typedef struct listdatastruct

list_data, *pnd_list_data;

// 定義標準解除函式

void classlistdatadestructor( void *p )

// 測試上面的**

void myclistclasstest()

小結

從前面的討論來看,似乎僅編寫乙個簡單的鍊錶就要做大量的工作,但這只須進行一次。很容易將這段**擴充為乙個處理排序、搜尋以及各種其他任務的 c++ 類,並且這個類可以處理任何資料物件或類(在乙個專案中,它處理大約二十個不同的物件)。您永遠不必重新編寫這段**。

技巧 在 C C 中如何構造通用的物件鍊錶

乙個簡化的問題示例 鍊錶的難點在於必須複製煉表處理函式來處理不同的物件,即便邏輯是完全相同的。例如 兩個結構類似的鍊錶 struct struct object a object a typedef struct struct object b object b 上面定義的兩個結構只有很小的一點差別...

技巧 在 C C 中如何構造通用的物件鍊錶

虛擬鍊錶和類鍊錶可以很好地實現這一點 您是否做過這樣乙個專案,它要求您在記憶體中儲存數目不定的若干不同物件?對於某些情況,二叉樹是最佳選擇,但在通常情況下,更簡單的鍊錶是顯而易見的選擇。鍊錶的難點在於必須複製煉表處理函式來處理不同的物件,即便邏輯是完全相同的。例如 兩個結構類似的鍊錶 struct ...

Linux中通用鍊錶 list 的解析(2)

1.下面介紹list的插入函式 ifndef config debug list static inline void list add struct list head new,struct list head prev,struct list head next else extern void...