12.08 Composition vs Inheritance: Wrapping List Primitives in Stack Interfaces
Executive Summary: Demonstrating the Adapter design pattern by implementing a
Stack interface over an underlying LinkedList.
💻 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
#ifndef STACK_H #define STACK_H class Stack { public: virtual void push(int newEntry) = 0; virtual int pop() = 0; virtual int peek() = 0; virtual bool isEmpty() const = 0; virtual void makeEmpty() = 0; }; #endif
#ifndef LIST_STACK_H #define LIST_STACK_H #include <iostream> #include "Stack.h" #include "LinkedList.h" using namespace std; class ListStack : public Stack { public: ListStack() {} //end ctor virtual ~ListStack() { makeEmpty(); }//end dtor void push(int newEntry) override { linkedListStack.add(newEntry, 0); }//end push int pop() override { if (isEmpty()) { cout << "Error: Cannot pop from an empty stack!" << endl; return 0; } return linkedListStack.remove(0); }//end pop int peek() override { if (isEmpty()) { cout << "Error: Cannot peek an empty stack!" << endl; return 0; } int data = linkedListStack.remove(0); linkedListStack.add(data, 0); return data; }//end peek bool isEmpty() const override { return linkedListStack.isEmpty(); } //end isEmpty void makeEmpty() override { linkedListStack.makeEmpty(); } private: LinkedList linkedListStack; }; #endif
#include <iostream> #include "ListStack.h" using namespace std; void printStack(ListStack& stack); int main() { ListStack stack; stack.push(100); stack.push(150); stack.push(222); stack.push(71); stack.push(53); stack.push(125); printStack(stack); cout << "Again to verify it's intact:" << endl; printStack(stack); //cout << "Top of stack is: " << stack.peek() << endl; //while (!stack.isEmpty()) { // cout << stack.pop() << endl; //} return 0; } void printStack(ListStack& stack) { ListStack temp; int data; while (!stack.isEmpty()) { data = stack.pop(); cout << data << endl; temp.push(data); }//end while while (!temp.isEmpty()) { stack.push(temp.pop()); } }//end printStack
📐 2. Architecture & UML Class Model
<<template class>>
LinkedList<T>
Adaptee Container
Attributes / Data Members
-headPtr : ListNode<T>*
-itemCount : size_t
Operations / Methods
+insert(pos: int, entry: const T&) : bool
+remove(pos: int) : bool
+getEntry(pos: int) : T const
+isEmpty() : bool const
<<template class>>
ListStack<T>
Stack Adapter
Attributes / Data Members
-listPtr : LinkedList<T>*
Operations / Methods
+ListStack()
+~ListStack()
+push(newEntry: const T&) : bool[delegates to listPtr->insert(1, entry)]
+pop() : bool[delegates to listPtr->remove(1)]
+peek() : T const[delegates to listPtr->getEntry(1)]
+isEmpty() : bool const
🔗 Architectural Relationships & Hierarchy
ListStack<T>
◆──
adapts interface (composition)
◆──
LinkedList<T>
📚 3. Core C++ Concepts Deep-Dive
The Adapter Pattern
Adapting an existing interface (List) to satisfy a target interface (Stack) using composition.
⚡ 4. Embedded Systems & Hardware Reality
Abstraction Cost
Inlined adapter wrappers have zero runtime cost under compiler optimization (-O2).
💡 5. Production-Ready Embedded Refactoring
💡 Production-Ready Refactor
template <typename Container> class StackAdapter { Container c_; public: void push(typename Container::value_type v) { c_.push_back(v); } };
📝 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. What design pattern does ListStack implement?
Detailed Explanation:
ListStack adapts the general
List interface into a restricted LIFO Stack interface.