Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
35
Добавлен:
10.09.2019
Размер:
9.28 Кб
Скачать
#include <iostream>
#include <stdexcept>

using std::cin;
using std::cout;
using std::endl;

// Очередь на основе однонаправленного списка
template <class T>
class Queue {

 protected:

    struct queue { // Структура "Queue"
        size_t n;
        T *elem;
        queue *child;
    };

 private:

    queue *front_elem_ = nullptr; // Первый элемент очереди
    queue *back_elem_ = nullptr; // Последний элемент очереди

 public:

    // Конструктор
    Queue() = default;

    // Конструктор копирования
    Queue(const Queue &arg) {
        if (arg.front_elem_) { // Если первый элемент очереди имеется
            queue *t = arg.front_elem_; // Заносим указатель на него в t
            for (size_t i = 0; i < arg.back_elem_->n; ++i) {
                push(*t->elem), t = t->child; // Добавляем очередной элемент и перемещаем t внутрь
            }
        }
    }

    // Оператор копирования
    Queue &operator=(const Queue &arg) {
        Queue temp(arg);
        swap(*this, temp);
        return *this;
    }

    // Конструктор перемещения
    Queue(Queue &&arg) noexcept {
        front_elem_ = arg.front_elem_;
        back_elem_ = arg.back_elem_;
        arg.back_elem_ = arg.front_elem_ = nullptr;
    }

    // Оператор перемещения
    Queue &operator=(Queue &&arg) noexcept {
        if (this != &arg) {
            swap(*this, arg); // Меняем значения
            arg.clear(); // Освобождаем то, что раньше лежало в this
        }
        return *this;
    }

    // Деструктор
    virtual ~Queue() {
        clear();
    }

    // Удаление всех элементов из очереди
    void clear() {
        if (front_elem_) { // Если очередь в this была, то освобождаем её
            queue *child = front_elem_->child; // Идем внутрь
            while (child) { // Пока можно идти внутрь (child != nullptr), идем
                delete child->elem; // Освобождаем память поля elem очередного элемента child
                queue *next = child->child; // next передвигается внутрь child-родителя
                delete child; // Освобождаем child-родителя
                child = next; // Переуказываем child на next (на ребенка только что освобожденного родителя)
            }
            delete front_elem_; // Освобождаем самый первый элемент
            front_elem_ = back_elem_ = nullptr; // Очищаем указатели
        }
    }

    // Вставляет элемент в конец
    Queue &push(const T &elem) {
        if (front_elem_) { // Если очередь в this была
            queue *t = back_elem_; // Получаем в t адрес, на который указывает back_elem_
            back_elem_ = new queue {back_elem_->n + 1, new T {elem}, nullptr}; // Создаем новый back_elem_
            t->child = back_elem_; // Переходим внутрь и перенаправляем на новый back_elem_
            return *this;
        }
        back_elem_ = front_elem_ =
            new queue {1, new T {elem}, nullptr}; // Если не было очереди, то создается первый элемент
        return *this;
    }

    // Доступ к первому элементу
    T &front() const noexcept(false) {
        if (front_elem_) {
            return *front_elem_->elem;
        }
        throw std::out_of_range("Queue. Method front. Out of range");
    }

    // Доступ к последнему элементу
    T &back() const noexcept(false) {
        if (back_elem_) {
            return *back_elem_->elem;
        }
        throw std::out_of_range("Queue. Method back. Out of range");
    }

    // Удаляет первый элемент
    void pop() noexcept(false) {
        if (front_elem_) { // Если первый элемент имеется
            delete front_elem_->elem; // Освобождаем его соответствующее поле
            queue *child = front_elem_->child; // Переходим к след. элементу (номер 2)
            delete front_elem_; // Освобождаем первый элемент
            if (front_elem_ != back_elem_) { // Если размер очереди был != 1
                front_elem_ = child; // Второй элемент становится первым
                for (size_t i = 1; i < back_elem_->n; ++i) {
                    child->n = i, child = child->child; // Меняем значения поля N для всех child
                }
                return;
            }
            back_elem_ = front_elem_ = nullptr; // Если размер очереди был = 1, то обнуление
            return;
        }
        throw std::out_of_range("Queue. Method pop. Out of range");
    }

    // Возвращает размер очереди
    size_t size() const {
        if (back_elem_) {
            return back_elem_->n;
        }
        return 0;
    }

    // True, если очередь пуста, иначе False
    bool empty() const noexcept {
        return front_elem_ == nullptr;
    }

    // Оператор приведения к типу bool
    explicit operator bool() {
        return front_elem_ != nullptr;
    }

    // Дружественный оператор для вывода содержимого очереди в поток ostream (cout, ...)
    friend std::ostream &operator<<(std::ostream &os, const Queue &arg) {
        queue *t = arg.front_elem_;
        if (t) {
            os << t->n << ": [" << *t->elem << "]";
            while (t->child) {
                os << ' ' << t->child->n << ": [" << *t->child->elem << "]";
                t = t->child;
            }
        }
        return os;
    }

    friend void swap(Queue &first, Queue &second) noexcept {
        std::swap(first.front_elem_, second.front_elem_);
        std::swap(first.back_elem_, second.back_elem_);
    }
};

int main() {
    Queue<double> queueA, queueB;
    // Добавление элементов на верх
    queueA.push(111).push(222).push(333).push(444);
    queueB.push(444).push(555).push(666);
    // Отображение элементов очереди на экране
    cout << "Queue A: " << queueA << endl;
    cout << "Queue B: " << queueB << endl;
    // Доступ к первому и последнему элементу очереди и удаление первого элемента
    cout << "Queue A [front() and back(), pop()]: " << queueA.front() << ", " << queueA.back() << endl;
    queueA.pop();
    cout << "Queue B [front() and back(), pop()]: " << queueB.front() << ", " << queueB.back() << endl;
    queueB.pop();
    // Конструктор копирования
    Queue<double> queueC(queueA);
    cout << "Queue (after copy): " << queueA << " (" << queueA.front() << ", " << queueA.back() << ")\n";
    cout << "Copy ^ (constructor): " << queueC << " (" << queueC.front() << ", " << queueC.back() << ")\n";
    // Оператор копирования
    queueC = queueB;
    cout << "Queue (after copy): " << queueB << " (" << queueB.front() << ", " << queueB.back() << ")\n";
    cout << "Copy ^ (assignment): " << queueC << " (" << queueC.front() << ", " << queueC.back() << ")\n";
    // Конструктор перемещения
    Queue<double> queueD(std::move(queueA));
    cout << "Queue (after move): " << queueA << endl;
    cout << "Move ^ (constructor): " << queueD << " (" << queueD.front() << ", " << queueD.back() << ")\n";
    // Оператор перемещения
    queueC = std::move(queueB);
    cout << "Queue (after move): " << queueB << endl;
    cout << "Move ^ (assignment): " << queueC << " (" << queueC.front() << ", " << queueC.back() << ")\n";
    // Размер очереди
    cout << "Queue D (size): " << queueD.size() << " (isEmpty ? " << ( queueD.empty() ? "TRUE" : "FALSE" ) << ")\n";
    cout << "Queue C (size): " << queueC.size() << " (isEmpty ? " << ( queueC.empty() ? "TRUE" : "FALSE" ) << ")\n";
    queueD.pop(), queueD.pop(), queueD.pop();
    queueC.clear();
    cout << "Queue D (size after pop): " << queueD.size() << " (isEmpty ? " << ( queueD.empty() ? "TRUE" : "FALSE" )
         << ")\n";
    cout << "Queue C (size after pop): " << queueC.size() << " (isEmpty ? " << ( queueC.empty() ? "TRUE" : "FALSE" )
         << ")\n";
    // Работа деструктора...
    queueD.push(111).push(222).push(333).push(444);
}
Соседние файлы в папке Решения лабораторных работ (местами есть ошибки)