12.05 Implementing Singly Linked Lists & Evaluating Intrusive Alternatives
Executive Summary: Building a full linked list data structure implementing
List<T>. We examine insertion/deletion at arbitrary positions, pointer manipulation, and intrusive list alternatives.
💻 1. Annotated Source Code
#ifndef LIST_H #define LIST_H class List { public: virtual void add(int newEntry) = 0; virtual void add(int newEntry, int position) = 0; virtual void set(int newEntry, int position) = 0; virtual bool contains(int entry) const = 0; virtual int find(int entry) const = 0; virtual int remove(int position) = 0; virtual void makeEmpty() = 0; virtual int size() const = 0; virtual bool isEmpty() const = 0; virtual void printList() const = 0; }; #endif
#ifndef LINKED_LIST_H #define LINKED_LIST_H #include <iostream> #include "List.h" using namespace std; // ---------------- Node -------------------- // class Node { public: Node(int data, Node* next) { this->data = data; this->next = next; }//end ctor int getData() const { return data; } void setData(int data) { this->data = data; } Node* getNext() const { return next; } void setNext(Node* next) { this->next = next; } private: int data; Node* next; }; // --------------- Linked List ---------------------// class LinkedList : public List { public: LinkedList() { mHead = nullptr; mNumElements = 0; }//end ctor virtual ~LinkedList() { makeEmpty(); //clean up the nodes }//end dtor void add(int newEntry) override { Node* newNode = new Node(newEntry, nullptr); if (mHead == nullptr) { mHead = newNode; } else { Node* walker = mHead; while (walker->getNext() != nullptr) { walker = walker->getNext(); }//end while walker->setNext(newNode); } mNumElements++; }//end add void add(int newEntry, int position) override { if (position < 0 || position > mNumElements) { cout << "Error: Cannot add at specified position." << endl; return; } Node* newNode = new Node(newEntry, nullptr); if (position == 0) { newNode->setNext(mHead); mHead = newNode; } else { Node* nodeBefore = mHead; for (int i = 0; i < position - 1; i++) { nodeBefore = nodeBefore->getNext(); }//end for newNode->setNext(nodeBefore->getNext()); nodeBefore->setNext(newNode); } mNumElements++; }//end add void set(int newEntry, int position) override { if (position < 0 || position > mNumElements) { cout << "Error: Invalid position." << endl; return; } Node* walker = mHead; for (int i = 0; i < position; i++) { walker = walker->getNext(); }//end for walker->setData(newEntry); }//end set bool contains(int entry) const override { return find(entry) != -1; }//end contains int find(int entry) const override { Node* walker = mHead; int index = 0; while (walker != nullptr) { if (walker->getData() == entry) { return index; } walker = walker->getNext(); index++; }//end while return -1; }//end find int remove(int position) override { if (position < 0 || position >= mNumElements) { cout << "Error: Cannot remove at specified position." << endl; return 0; } int dataToReturn = 0; if (position == 0) { Node* temp = mHead; dataToReturn = temp->getData(); mHead = mHead->getNext(); delete temp; } else { Node* nodeBefore = mHead; for (int i = 0; i < position - 1; i++) { nodeBefore = nodeBefore->getNext(); }//end for Node* nodeToRemove = nodeBefore->getNext(); Node* nodeAfter = nodeToRemove->getNext(); dataToReturn = nodeToRemove->getData(); nodeBefore->setNext(nodeAfter); delete nodeToRemove; } mNumElements--; return dataToReturn; }//end remove void makeEmpty() override { Node* temp; while (mHead != nullptr) { temp = mHead; mHead = mHead->getNext(); delete temp; } }//end makeEmpty int size() const override { return mNumElements; }//end size bool isEmpty() const override { return mNumElements == 0; }//end isEmpty void printList() const override { Node* walker = mHead; while (walker != nullptr) { cout << walker->getData() << endl; walker = walker->getNext(); }//end while } private: Node* mHead; int mNumElements; }; #endif
#include <iostream> #include "LinkedList.h" using namespace std; int main() { LinkedList myList; int data; myList.add(15); myList.add(22); myList.add(100); myList.add(44); myList.add(500); myList.add(444); myList.add(505); myList.add(22); myList.set(1515, 0); myList.set(2222, 7); myList.printList(); myList.add(1500, 89); cout << endl; myList.printList(); cout << endl; int lastIndex = myList.size() - 1; int removed = myList.remove(lastIndex); cout << "Just removed " << removed << " from the END of the list." << endl; while (!myList.isEmpty()) { data = myList.remove(0); cout << "Just removed: " << data << endl; }//end while cout << endl << "Final contents (should be empty):" << endl; myList.printList(); return 0; }
📐 2. Architecture & UML Class Model
<<struct>>
ListNode<T>
Linked Node
Attributes / Data Members
+item : T
+next : ListNode<T>*
Operations / Methods
+ListNode(item: const T&, next: ListNode<T>* = nullptr)
<<template class>>
LinkedList<T>
Dynamic Linked List
Attributes / Data Members
-headPtr : ListNode<T>*
-itemCount : size_t = 0
Operations / Methods
+LinkedList()
+~LinkedList()
+insert(newPosition: int, newEntry: const T&) : bool[O(N)]
+remove(position: int) : bool[O(N)]
+getEntry(position: int) : T const[O(N)]
+clear() : void
+isEmpty() : bool const
+getLength() : size_t const
🔗 Architectural Relationships & Hierarchy
LinkedList<T>
◆──
manages heap chain
◆──
ListNode<T>
📚 3. Core C++ Concepts Deep-Dive
List Traversal and Modification
Inserting at the head is $O(1)$, while arbitrary index access requires $O(N)$ linear traversal.
⚡ 4. Embedded Systems & Hardware Reality
Intrusive Linked Lists (Embedded Gold Standard)
In operating systems (FreeRTOS, Linux Kernel), nodes are embedded directly inside existing structures (intrusive lists), eliminating extra heap allocations entirely!
💡 5. Production-Ready Embedded Refactoring
💡 Production-Ready Refactor
struct TaskControlBlock { TaskControlBlock* nextReadyTask; uint32_t taskId; }; // Zero heap overhead intrusive node!
📝 Knowledge Verification Quiz
Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.
Q1. Why do embedded kernels (like FreeRTOS) use intrusive linked lists instead of standard std::list?
Detailed Explanation:
Intrusive lists avoid dynamic node wrapper allocation by placing pointer hooks inside the data structure itself.