C++ Linked Lists   «Prev  Next»
Lesson 1

Useful classes for dynamic structures

This module examines two useful class implementations of dynamic structures:
  1. a "safe array" that performs its own bounds checking and
  2. a singly linked list.

In addition, you will take a look at an efficient object disposal routine for large aggregate types. You will learn how to:
  1. Implement a bounds-checking array using classes
  2. Group classes in a hasa relationship
  3. Implement a singly linked list using classes
  4. Use reference counting for efficient object disposal

C++ Safe Array

Here are the key characteristics of a "Safe Array" in C++:
  1. Bounds Checking
    • Primary Feature: Safe Arrays automatically perform bounds checking when you access elements. This helps prevent common programming errors like buffer overflows that can lead to crashes or security vulnerabilities.
    • How it Works: Safe Arrays store information about the array's dimensions and bounds along with the data itself. Attempts to access elements outside the defined bounds of the array result in exceptions being thrown.
  2. Simplified Management:
    • COM Wrappers: Safe Arrays are often implemented as part of COM (Component Object Model) technology, providing a standardized way to pass arrays between different programming languages or modules within COM.
    • Automated Memory Management: Safe Arrays can handle memory management, including allocation and deallocation, reducing the risk of memory leaks.
  3. Performance Trade-Off:
    • Overhead: Bounds checking and COM-related management introduce overhead compared to raw C++ arrays.
    • Consider Usage: The safety benefits of Safe Arrays may outweigh the performance hit in contexts where security and reliability are paramount.
  4. Libraries and Support:
    • ATL Libraries: Microsoft's Active Template Library (ATL) provides classes like `CComSafeArray` for working with Safe Arrays in C++.
    • Windows API: Certain Windows API functions specifically expect and work with Safe Arrays for secure parameter passing.
When to Use Safe Arrays
  • COM Interoperability: When you need to exchange arrays between COM components, potentially written in different languages.
  • Security-Critical Applications: Where preventing buffer overflows and ensuring data integrity is paramount.
  • Reducing Development Effort: If the safety benefits outweigh the slight performance overhead

Alternatives
  • Modern C++ Libraries: Libraries like `std::vector` and `std::array` provide bounds checking and memory management features, often with better performance characteristics than older-style Safe Arrays.

Linked List

Creating a singly linked list in C++ involves defining a `Node` structure and a `LinkedList` class that manipulates these nodes. The `Node` structure typically contains the data you want to store and a pointer to the next node. The `LinkedList` class manages the operations such as adding, removing, and searching for elements within the list.
Below is an example implementation of a singly linked list in C++ with basic operations such as adding to the end, printing the list, and a constructor/destructor for proper memory management.
#include <iostream>

// Definition of the Node structure
struct Node {
    int data;       // The data held by the Node
    Node* next;     // Pointer to the next node in the list

    // Constructor to initialize a Node with data and optionally set the next node
    Node(int data, Node* next = nullptr) : data(data), next(next) {}
};

// Definition of the LinkedList class
class LinkedList {
public:
    Node* head;  // The head pointer points to the first element in the list

    // Constructor to initialize an empty list
    LinkedList() : head(nullptr) {}

    // Destructor to free the memory used by the list
    ~LinkedList() {
        Node* current = head;
        while (current != nullptr) {
            Node* next = current->next;
            delete current;
            current = next;
        }
        head = nullptr;
    }

    // Function to add a new element to the end of the list
    void append(int data) {
        if (head == nullptr) {
            head = new Node(data);
        } else {
            Node* current = head;
            while (current->next != nullptr) {
                current = current->next;
            }
            current->next = new Node(data);
        }
    }

    // Function to print all the elements in the list
    void print() const {
        Node* current = head;
        while (current != nullptr) {
            std::cout << current->data << " ";
            current = current->next;
        }
        std::cout << std::endl;
    }
};

int main() {
    LinkedList list;

    list.append(1);
    list.append(2);
    list.append(3);

    list.print();  // Should print: 1 2 3

    return 0;
}

Explanation:
  • Node Structure: Represents each element in the list. It contains the data (`int data`) and a pointer to the next node (`Node* next`).
  • LinkedList Class: Manages the list. It only contains a single member `Node* head`, which points to the first element of the list.
  • LinkedList Constructor: Initializes the `head` pointer to `nullptr`, indicating that the list is initially empty.
  • LinkedList Destructor: Iterates through the list, deallocating memory used by each node to prevent memory leaks.
  • `append` Function: Adds a new node to the end of the list. If the list is empty (`head == nullptr`), it creates a new node and assigns it to `head`. Otherwise, it traverses the list until it finds the last node and attaches a new node to its `next` pointer.
  • `print` Function: Iterates over each element in the list, starting from `head`, and prints the `data` of each node.

This code provides a basic structure for a singly linked list, and you can extend it with more functionalities such as deleting nodes, finding elements, or inserting elements at a given position.

SEMrush Software