10. Initializer Lists and Dynamic Data Structures

Using std::initializer_list

std::initializer_list allows you to initialize an object with a list of values in a convenient way. This often appears in constructors where multiple arguments need to be passed without manually specifying individual parameters.

Key Points:

Simple Example

#include <iostream>
#include <initializer_list>
using namespace std;

class Example {
public:
    Example(initializer_list<int> list) {
        cout << "Initializing with the following values:" << endl;
        for (auto val : list) {
            cout << val << " ";
        }
        cout << endl;
    }
};

int main() {
    // This calls Example(initializer_list<int>)
    Example ex{1, 2, 3, 4, 5};
    return 0;
}

Custom Fixed Array Example

Here’s a class FixedArray that uses an initializer_list for initialization. It stores a fixed number of elements in an internal array.

#include <initializer_list>
#include <iostream>
using namespace std;

template<class T, int size>
class FixedArray {
    T arr[size]{};
    size_t mysize = 0;

public:
    // Constructor that initializes the array with a list of values
    FixedArray(initializer_list<T> list) {
        for (auto data : list) {
            arr[mysize] = data;
            mysize++;
        }
    }

    void Show() const {
        for (size_t x = 0; x < mysize; x++) {
            cout << arr[x] << " ";
        }
        cout << endl;
    }

    // Overloaded operator[] to access elements by index
    T& operator[](int index) {
        return arr[index];
    }
};

int main() {
    // Initialize the FixedArray with a list of integers
    FixedArray<int, 20> myarray{1, 3, 4, 6, 2, 6, 2, 6};
    myarray.Show();   // Displays the elements

    return 0;
}

Key Takeaways:

Dynamic Data Structures Overview

When dealing with dynamic data structures, you decide how and where data is stored in memory—often on the heap—allowing flexible sizing and more complex behaviors. Common examples include:

  1. Stack (FILO: First In, Last Out)
  1. Queue (FIFO: First In, First Out)
  1. Dequeue (Double-Ended Queue)
  1. Priority Queue
  1. Linked List (Singly Linked, Doubly Linked, etc.)
  1. Trees (Binary, AVL, Red-Black, etc.)

Stack vs. Queue

Implementing a Custom Stack

Below is a simple Stack class that dynamically grows as items are pushed. This implementation uses templates to store any data type.

#include <iostream>
#include <assert.h>
using namespace std;

template <class T, int sizeLimit>
class Stack {
private:
    T* data;
    size_t size;

public:
    Stack() : data(nullptr), size(0) {}

    void push(const T& value) {
        cout << "Value added successfully!" << endl;
        // Create a new array with one extra space
        T* newarr = new T[size + 1] {};
        for (size_t x = 0; x < size; x++) {
            newarr[x] = data[x];
        }
        newarr[size] = value;
        // Clean up old memory
        if (size != 0) {
            delete[] data;
        }
        data = newarr;
        newarr = nullptr;
        size++;
    }

    T pop() {
        assert(size > 0 && "Cannot pop from an empty stack!");
        // Create a smaller array
        T* newarr = new T[size - 1] {};
        for (size_t x = 0; x < size - 1; x++) {
            newarr[x] = data[x];
        }
        // Extract the last element
        T last = data[size - 1];
        delete[] data;
        data = newarr;
        newarr = nullptr;
        size--;
        return last;
    }

    // Returns the last element without removing it
    T& peek() {
        assert(size > 0 && "Cannot peek at an empty stack!");
        return data[size - 1];
    }

    T& operator[](int index) {
        return data[index];
    }

    size_t GetSize() const {
        return size;
    }

    void Clear() {
        if (data == nullptr && size == 0) {
            return;
        }
        delete[] data;
        data = nullptr;
        size = 0;
    }

    ~Stack() {
        delete[] data;
    }
};

// Class to demonstrate storing objects in the stack
class User {
    string name;
    string surname;
public:
    User() {}
    User(const string& name, const string& surname) {
        this->name = name;
        this->surname = surname;
    }
    void ShowUser() const {
        cout << "===== USER =====" << endl;
        cout << "Name: " << this->name << endl;
        cout << "Surname: " << this->surname << endl;
    }
};

int main() {
    Stack<int, 10> mystack;
    mystack.push(10);
    mystack.push(11);
    mystack.push(12);

    // Pop and display the last value
    // int lastint = mystack.pop();
    // cout << "Last int : " << lastint << endl;

    Stack<char, 10> mystack2;
    mystack2.push('A');
    mystack2.push('B');
    mystack2.push('C');
    char lastchar = mystack2.pop();
    cout << "Last char : " << lastchar << endl;

    Stack<User, 3> mystack3;
    User u1("Tofiq", "Tofiqli");
    User u2("A", "Ali");
    User u3("John", "Johnlu");

    mystack3.push(u1);
    mystack3.push(u2);
    mystack3.push(u3);

    User lastuser = mystack3.pop();
    cout << "Last User: " << endl;
    lastuser.ShowUser();

    return 0;
}

Highlights:

Implementing a Custom Queue

A Queue processes elements in the order they arrive (FIFO: First In, First Out). Here’s an example implementation using an array for storage:

#include <iostream>
#include <assert.h>
#include <windows.h>  // For Sleep()
using namespace std;

template <class T>
class Queue {
    T* arr;
    int capacity;
    int front;
    int rear;
    int count;

public:
    Queue(int size) {
        arr = new T[size] {};
        capacity = size;
        front = 0;
        rear = -1;
        count = 0;
    }

    void enqueue(T value) {
        assert(!IsFull() && "Queue is full!");
        arr[++rear] = value;
        ++count;
    }

    void dequeue() {
        assert(!IsEmpty() && "Queue is empty!");
        // Shift elements to remove the front
        auto temp = new T[count - 1] {};
        for (int x = 0; x < count - 1; x++) {
            temp[x] = arr[x + 1];
        }
        delete[] arr;
        arr = temp;
        temp = nullptr;
        --count;
        rear--;
    }

    T peek() {
        assert(!IsEmpty() && "Queue is empty!");
        return arr[front];
    }

    bool IsFull() {
        return size() == capacity;
    }

    bool IsEmpty() const {
        return size() == 0;
    }

    int size() const {
        return count;
    }

    ~Queue() {
        delete[] arr;
    }
};

// Example class to enqueue
class Person {
    string name;
    string surname;
    double money;
public:
    Person() = default;
    Person(const string& name, const string& surname, double money) {
        SetName(name);
        SetSurname(surname);
        SetMoney(money);
    }

    void SetName(const string& name) { this->name = name; }
    void SetSurname(const string& surname) { this->surname = surname; }
    void SetMoney(double money) { this->money = money; }

    string GetName() const { return name; }
    string GetSurname() const { return surname; }
    double GetMoney() const { return money; }

    friend ostream& operator<<(ostream& out, const Person& p) {
        out << "======= PERSON INFO =======" << endl;
        out << "Name: " << p.GetName() << endl;
        out << "Surname: " << p.GetSurname() << endl;
        out << "Money: " << p.GetMoney() << endl;
        return out;
    }
};

int main() {
    Person p1("John", "Johnlu", 1000);
    Person p2("Mike", "Eliyev", 200);
    Person p3("Hakuna", "Matata", 100);

    // Create a Queue with capacity 3
    Queue<Person> persons(3);
    persons.enqueue(p1);
    persons.enqueue(p2);
    persons.enqueue(p3);

    // Process the queue
    while (!persons.IsEmpty()) {
        auto person = persons.peek();
        cout << person << endl;
        Sleep(1500); // Pause for 1.5 seconds
        cout << "His work finished\n" << endl;
        persons.dequeue();
    }

    return 0;
}

Key Aspects:

Other Data Structures

While Stacks and Queues are fundamental, you can also explore:

Reference

The content in this document is based on the original notes provided in Azerbaijani. For further details, you can refer to the original document using the following link:

Original Note - Azerbaijani Version