T. W. Burger (twburger@bigfoot.com), boss, Thomas Wolfgang burger Consulting
September 01, 2000
Have you ever done a project that requires you to save a certain number of different objects in the memory? In some cases, binary trees are the best choice, but in general, a simpler linked list is an obvious choice.
Example of a simplified Problem
The difficulty of a linked list is that you must copy the linked list processing function to process different objects, even if the logic is identical. For example:
Two linked lists with similar structures
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;
|
The two structures defined above have only a small difference. The difference between object_ B and object_a is only one integer variable. However, in the compiler's view, they are still very different. Each object stored in the linked list must be copied to add, delete, and search functions for the linked list. To solve this problem, you can use a union or structure with all three variables. The integer c is not used in all cases. This may become very complex and lead to a bad programming style.
C code solution: Virtual linked list
One of the better solutions to this problem is the virtual linked list. A virtual linked list contains only linked list pointers. Objects are stored behind the linked list structure. This is implemented in this way. First, allocate memory for the linked list node, then allocate memory for the object, and then allocate the memory to the linked list node pointer, as shown below:
An Implementation of the virtual linked list Structure
Typedef struct liststruct { Liststruct * next; } List, * plist; Plist head = NULL; Plist addtolist (plist head, void * data, size_t datasize) { Plist newlist = NULL; Void * P; // Allocate node memory and data memory Newlist = (plist) malloc (datasize + sizeof (list )); // Specify a pointer for this data buffer P = (void *) (newlist + 1 ); // Copy data Memcpy (p, Data, datasize ); // Specify this node to the table header of the linked list. If (head) { Newlist-> next = head; } Else Newlist-> next = NULL; Head = newlist; Return head; }
|
The linked list node is now built on the data value copy. This version can well process scalar values, but cannot process objects with elements allocated using malloc or new. To process these objects, the list structure must contain a general de-function pointer, which can be used to release memory (or close files) before deleting a node from the linked list and removing it, or call the close method ).
A linked list with a lift Function
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; // Allocate node memory and data memory Newlist = (plist) malloc (datasize + sizeof (list )); // Specify a pointer for this data buffer P = (void *) (newlist + 1 ); // Copy data Memcpy (p, Data, datasize ); Newlist-> destructfunc = destructor;
// Specify this node to the table header of the linked list. 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) { // Type conversion for node pointers Plist PL = (plist) P; // Type conversion for data pointers 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 ); // This object has been copied and is now deleted from the original object 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; // Release the linked list Deletelist (head ); }
|
The same pointer containing the same unfunction in each linked list node seems to be a waste of memory space. This is true, but this is the case only when the linked list always contains the same object. Writing a linked list in this way allows you to place any object in any position in the linked list. Most linked list functions require that objects are always of the same type or class. The virtual linked list does not have this requirement. All it needs is a method that separates objects from each other. To achieve this, you can detect and remove the function pointer value, or add a type value before all the structures used in the linked list. Of course, if you want to write a linked list as a C ++ class, you can only set and store the pointer pointing to the unfunction once.
C ++ solution: Linked List
This solution defines the clist class as a class exported from the list structure. It processes a single storage type by storing a single value of the relief function. Note that the added getcurrentdata () function completes the mathematical conversion from the linked list node pointer to the data offset pointer.
A virtual linked list object
// Define the release function pointer Typedef void (* listnodedestructor) (void *); // The linked list with the function pointer removed is not added. Typedef struct ndliststruct { Ndliststruct * next; } Nd_list, * pnd_list; // Define a linked list for processing a data type 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; }; // Construct the linked list object with the correct starting value Clist: clist (listnodedestructor destructor) : M_headoflist (null), m_currentnode (null) { M_destructfunc = destructor; } // Delete the linked list after removing the object Clist ::~ Clist () { Deletelist (m_headoflist ); } // Add a new node to the linked list Pnd_list clist: addtolist (void * data, size_t datasize) { Pnd_list newlist = NULL; Void * P; // Allocate node memory and data memory Newlist = (pnd_list) malloc (datasize + sizeof (nd_list )); // Specify a pointer for this data buffer P = (void *) (newlist + 1 ); // Copy data Memcpy (p, Data, datasize ); // Specify this node to the table header of the linked list. If (m_headoflist) { Newlist-> next = m_headoflist; } Else Newlist-> next = NULL; M_headoflist = newlist; Return m_headoflist; } // Return the current node data as the void type so that the calling function can convert it to any type Void * clist: getcurrentdata () { Return (void *) (m_currentnode + 1 ); } // Delete the allocated linked list Void clist: deletelist (pnd_list head) { Pnd_list next; While (head) { Next = head-> next; M_destructfunc (void *) Head ); Free (head ); Head = next; } } // Create a structure to be created and stored in the linked list Typedef struct listdatastruct { Lpstr P; } List_data, * pnd_list_data; // Define standard release functions Void classlistdatadestructor (void * P) { // Type conversion for node pointers Pnd_list PL = (pnd_list) P; // Type conversion for data pointers Pnd_list_data PLD = (pnd_list_data) (PL + 1 ); Delete PLD-> P; } // Test the above Code Void myclistclasstest () { // Create a linked list Clist * pa_list_of_data = new clist (classlistdatadestructor ); // Create a Data Object
Pnd_list_data d = new list_data; D-> P = new char [24]; Strcpy (D-> P, "hello "); // Create a local pointer pointing to the top of the linked list Pnd_list head = NULL; // Add some data to the linked list Head = pa_list_of_data-> addtolist (void *) d, Sizeof (pnd_list_data )); // This object has been copied and is now deleted from the original object Delete D; // Confirm that it has been stored 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 )); // This object has been copied and is now deleted from the original object Delete D; // Confirm that it has been stored P = (pnd_list_data) pa_list_of_data-> getcurrentdata ()-> P; // Delete the Linked List class. The Destructor deletes the linked list. Delete pa_list_of_data; }
|
Summary
From the previous discussion, it seems that writing a simple linked list only requires a lot of work, but this only needs to be done once. It is easy to expand this code into a C ++ class that processes sorting, search, and various other tasks, and this class can process any data object or class (in a project, it processes about 20 different objects ). You never have to rewrite this code.