假設有兩個結構僅差一個欄位:
struct Struct_Object_A { int a, b; Struct_Object_A *next; };
struct Struct_Object_B { int a, b, c; Struct_Object_B *next; };
雖然邏輯相同,編譯器視為完全不同型別,導致新增、刪除、搜尋等函式必須重複撰寫。傳統解法使用聯合或冗餘欄位,但會造成程式碼膨脹與維護困難。
虛擬鏈表節點僅包含指標,實際資料儲存在節點結構之後的記憶體區塊:
typedef struct liststruct { liststruct *next; } LIST, *pLIST;
pLIST AddToList(pLIST Head, void *data, size_t datasize) {
pLIST newlist = (pLIST)malloc(datasize + sizeof(LIST));
void *p = (void*)(newlist + 1); // 指向資料區
memcpy(p, data, datasize); // 複製資料
newlist->next = Head;
return newlist; // 新節點成為表頭
}
為處理動態配置記憶體的物件(需釋放資源),節點加入解構函式指標:
typedef void (*ListNodeDestructor)(void*);
typedef struct liststruct {
ListNodeDestructor DestructFunc;
liststruct *next;
} LIST, *pLIST;
void DeleteList(pLIST Head) {
while (Head) {
pLIST Next = Head->next;
Head->DestructFunc((void*)Head); // 呼叫自訂清理邏輯
free(Head);
Head = Next;
}
}
測試案例展示字串資料的正確配置與釋放流程。
ND_LIST 僅含 next 指標CList 類別繼承 ND_LIST,封裝單一資料型別的解構函式GetCurrentData() 完成節點指標到資料偏移的轉換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, m_CurrentNode;
ListNodeDestructor m_DestructFunc;
};
// 關鍵:節點指標轉資料指標的數學運算
void *CList::GetCurrentData() {
return (void*)(m_CurrentNode + 1); // 跳過節點結構體大小
}
CList* pList = new CList(ClassListDataDestructor);
pND_LIST_DATA d = new LIST_DATA;
d->p = new char[24];
strcpy(d->p, "Hello");
pList->AddToList((void*)d, sizeof(pND_LIST_DATA));
delete d; // 原物件可刪除,資料已複製至鏈表節點
虛擬鏈表透過「節點前置、資料後置」的記憶體佈局,配合函式指標實現多型銷毀,完美解決 C/C++ 靜態型別系統下的通用容器難題。C++ 封裝進一步提升型別安全與介面清晰度。這套架構雖初期投入較大,但長期效益極高,是系統級程式設計的經典模式。