Skip to content

Instantly share code, notes, and snippets.

@NTG-TPL
Last active October 2, 2023 09:47
Show Gist options
  • Save NTG-TPL/b0b021dc1ed8cfc2e1886fc5c0f3a04b to your computer and use it in GitHub Desktop.
Save NTG-TPL/b0b021dc1ed8cfc2e1886fc5c0f3a04b to your computer and use it in GitHub Desktop.
simple linked list C++ implementation
#include "single-linked-list.h"
#include <cstdalign>
void Test0() {
using namespace std;
{
const SingleLinkedList<int> empty_int_list;
assert(empty_int_list.GetSize() == 0u);
assert(empty_int_list.IsEmpty());
}
{
const SingleLinkedList<string> empty_string_list;
assert(empty_string_list.GetSize() == 0u);
assert(empty_string_list.IsEmpty());
}
}
// Эта функция проверяет работу класса SingleLinkedList
void Test() {
struct DeletionSpy {
~DeletionSpy() {
if (deletion_counter_ptr) {
++(*deletion_counter_ptr);
}
}
int* deletion_counter_ptr = nullptr;
};
// Проверка PopFront
{
SingleLinkedList<int> numbers{3, 14, 15, 92, 6};
numbers.PopFront();
assert((numbers == SingleLinkedList<int>{14, 15, 92, 6}));
SingleLinkedList<DeletionSpy> list;
list.PushFront(DeletionSpy{});
int deletion_counter = 0;
list.begin()->deletion_counter_ptr = &deletion_counter;
assert(deletion_counter == 0);
list.PopFront();
assert(deletion_counter == 1);
}
// Доступ к позиции, предшествующей begin
{
SingleLinkedList<int> empty_list;
const auto& const_empty_list = empty_list;
assert(empty_list.before_begin() == empty_list.cbefore_begin());
assert(++empty_list.before_begin() == empty_list.begin());
assert(++empty_list.cbefore_begin() == const_empty_list.begin());
SingleLinkedList<int> numbers{1, 2, 3, 4};
const auto& const_numbers = numbers;
assert(numbers.before_begin() == numbers.cbefore_begin());
assert(++numbers.before_begin() == numbers.begin());
assert(++numbers.cbefore_begin() == const_numbers.begin());
}
// Вставка элемента после указанной позиции
{ // Вставка в пустой список
{
SingleLinkedList<int> lst;
const auto inserted_item_pos = lst.InsertAfter(lst.before_begin(), 123);
assert((lst == SingleLinkedList<int>{123}));
assert(inserted_item_pos == lst.begin());
assert(*inserted_item_pos == 123);
}
// Вставка в непустой список
{
SingleLinkedList<int> lst{1, 2, 3};
auto inserted_item_pos = lst.InsertAfter(lst.before_begin(), 123);
assert(inserted_item_pos == lst.begin());
assert(inserted_item_pos != lst.end());
assert(*inserted_item_pos == 123);
assert((lst == SingleLinkedList<int>{123, 1, 2, 3}));
inserted_item_pos = lst.InsertAfter(lst.begin(), 555);
assert(++SingleLinkedList<int>::Iterator(lst.begin()) == inserted_item_pos);
assert(*inserted_item_pos == 555);
assert((lst == SingleLinkedList<int>{123, 555, 1, 2, 3}));
};
}
// Вспомогательный класс, бросающий исключение после создания N-копии
struct ThrowOnCopy {
ThrowOnCopy() = default;
explicit ThrowOnCopy(int& copy_counter) noexcept
: countdown_ptr(&copy_counter) {
}
ThrowOnCopy(const ThrowOnCopy& other)
: countdown_ptr(other.countdown_ptr) //
{
if (countdown_ptr) {
if (*countdown_ptr == 0) {
throw std::bad_alloc();
} else {
--(*countdown_ptr);
}
}
}
// Присваивание элементов этого типа не требуется
ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
// Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
// Как только обнулится, конструктор копирования выбросит исключение
int* countdown_ptr = nullptr;
};
// Проверка обеспечения строгой гарантии безопасности исключений
{
bool exception_was_thrown = false;
for (int max_copy_counter = 10; max_copy_counter >= 0; --max_copy_counter) {
SingleLinkedList<ThrowOnCopy> list{ThrowOnCopy{}, ThrowOnCopy{}, ThrowOnCopy{}};
try {
int copy_counter = max_copy_counter;
list.InsertAfter(list.cbegin(), ThrowOnCopy(copy_counter));
assert(list.GetSize() == 4u);
} catch (const std::bad_alloc&) {
exception_was_thrown = true;
assert(list.GetSize() == 3u);
break;
}
}
assert(exception_was_thrown);
}
// Удаление элементов после указанной позиции
{
{
SingleLinkedList<int> lst{1, 2, 3, 4};
const auto& const_lst = lst;
const auto item_after_erased = lst.EraseAfter(const_lst.cbefore_begin());
assert((lst == SingleLinkedList<int>{2, 3, 4}));
assert(item_after_erased == lst.begin());
}
{
SingleLinkedList<int> lst{1, 2, 3, 4};
const auto item_after_erased = lst.EraseAfter(lst.cbegin());
assert((lst == SingleLinkedList<int>{1, 3, 4}));
assert(item_after_erased == (++lst.begin()));
}
{
SingleLinkedList<int> lst{1, 2, 3, 4};
const auto item_after_erased = lst.EraseAfter(++(++lst.cbegin()));
assert((lst == SingleLinkedList<int>{1, 2, 3}));
assert(item_after_erased == lst.end());
}
{
SingleLinkedList<DeletionSpy> list{DeletionSpy{}, DeletionSpy{}, DeletionSpy{}};
auto after_begin = ++list.begin();
int deletion_counter = 0;
after_begin->deletion_counter_ptr = &deletion_counter;
assert(deletion_counter == 0u);
list.EraseAfter(list.cbegin());
assert(deletion_counter == 1u);
}
}
}
// Эта функция тестирует работу SingleLinkedList
void Test1() {
// Шпион, следящий за своим удалением
struct DeletionSpy {
DeletionSpy() = default;
explicit DeletionSpy(int& instance_counter) noexcept
: instance_counter_ptr_(&instance_counter) //
{
OnAddInstance();
}
DeletionSpy(const DeletionSpy& other) noexcept
: instance_counter_ptr_(other.instance_counter_ptr_) //
{
OnAddInstance();
}
DeletionSpy& operator=(const DeletionSpy& rhs) noexcept {
if (this != &rhs) {
auto rhs_copy(rhs);
std::swap(instance_counter_ptr_, rhs_copy.instance_counter_ptr_);
}
return *this;
}
~DeletionSpy() {
OnDeleteInstance();
}
private:
void OnAddInstance() noexcept {
if (instance_counter_ptr_) {
++(*instance_counter_ptr_);
}
}
void OnDeleteInstance() noexcept {
if (instance_counter_ptr_) {
assert(*instance_counter_ptr_ != 0);
--(*instance_counter_ptr_);
}
}
int* instance_counter_ptr_ = nullptr;
};
// Проверка вставки в начало
{
SingleLinkedList<int> l;
assert(l.IsEmpty());
assert(l.GetSize() == 0u);
l.PushFront(0);
l.PushFront(1);
assert(l.GetSize() == 2);
assert(!l.IsEmpty());
l.Clear();
assert(l.GetSize() == 0);
assert(l.IsEmpty());
}
// Проверка фактического удаления элементов
{
int item0_counter = 0;
int item1_counter = 0;
int item2_counter = 0;
{
SingleLinkedList<DeletionSpy> list;
list.PushFront(DeletionSpy{item0_counter});
list.PushFront(DeletionSpy{item1_counter});
list.PushFront(DeletionSpy{item2_counter});
assert(item0_counter == 1);
assert(item1_counter == 1);
assert(item2_counter == 1);
list.Clear();
assert(item0_counter == 0);
assert(item1_counter == 0);
assert(item2_counter == 0);
list.PushFront(DeletionSpy{item0_counter});
list.PushFront(DeletionSpy{item1_counter});
list.PushFront(DeletionSpy{item2_counter});
assert(item0_counter == 1);
assert(item1_counter == 1);
assert(item2_counter == 1);
}
assert(item0_counter == 0);
assert(item1_counter == 0);
assert(item2_counter == 0);
}
// Вспомогательный класс, бросающий исключение после создания N-копии
struct ThrowOnCopy {
ThrowOnCopy() = default;
explicit ThrowOnCopy(int& copy_counter) noexcept
: countdown_ptr(&copy_counter) {
}
ThrowOnCopy(const ThrowOnCopy& other)
: countdown_ptr(other.countdown_ptr) //
{
if (countdown_ptr) {
if (*countdown_ptr == 0) {
throw std::bad_alloc();
} else {
--(*countdown_ptr);
}
}
}
// Присваивание элементов этого типа не требуется
ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
// Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
// Как только обнулится, конструктор копирования выбросит исключение
int* countdown_ptr = nullptr;
};
{
bool exception_was_thrown = false;
// Последовательно уменьшаем счётчик копирований до нуля, пока не будет выброшено исключение
for (int max_copy_counter = 5; max_copy_counter >= 0; --max_copy_counter) {
// Создаём непустой список
SingleLinkedList<ThrowOnCopy> list;
list.PushFront(ThrowOnCopy{});
try {
int copy_counter = max_copy_counter;
list.PushFront(ThrowOnCopy(copy_counter));
// Если метод не выбросил исключение, список должен перейти в новое состояние
assert(list.GetSize() == 2);
} catch (const std::bad_alloc&) {
exception_was_thrown = true;
// После выбрасывания исключения состояние списка должно остаться прежним
assert(list.GetSize() == 1);
break;
}
}
assert(exception_was_thrown);
}
}
// Эта функция тестирует работу SingleLinkedList
void Test2() {
// Итерирование по пустому списку
{
SingleLinkedList<int> list;
// Константная ссылка для доступа к константным версиям begin()/end()
const auto& const_list = list;
// Итераторы begin и end у пустого диапазона равны друг другу
assert(list.begin() == list.end());
assert(const_list.begin() == const_list.end());
assert(list.cbegin() == list.cend());
assert(list.cbegin() == const_list.begin());
assert(list.cend() == const_list.end());
}
// Итерирование по непустому списку
{
SingleLinkedList<int> list;
const auto& const_list = list;
list.PushFront(1);
assert(list.GetSize() == 1u);
assert(!list.IsEmpty());
assert(const_list.begin() != const_list.end());
assert(const_list.cbegin() != const_list.cend());
assert(list.begin() != list.end());
assert(const_list.begin() == const_list.cbegin());
assert(*list.cbegin() == 1);
*list.begin() = -1;
assert(*list.cbegin() == -1);
const auto old_begin = list.cbegin();
list.PushFront(2);
assert(list.GetSize() == 2);
const auto new_begin = list.cbegin();
assert(new_begin != old_begin);
// Проверка прединкремента
{
auto new_begin_copy(new_begin);
assert((++(new_begin_copy)) == old_begin);
}
// Проверка постинкремента
{
auto new_begin_copy(new_begin);
assert(((new_begin_copy)++) == new_begin);
assert(new_begin_copy == old_begin);
}
// Итератор, указывающий на позицию после последнего элемента, равен итератору end()
{
auto old_begin_copy(old_begin);
assert((++old_begin_copy) == list.end());
}
}
// Преобразование итераторов
{
SingleLinkedList<int> list;
list.PushFront(1);
// Конструирование ConstIterator из Iterator
SingleLinkedList<int>::ConstIterator const_it(list.begin());
assert(const_it == list.cbegin());
assert(*const_it == *list.cbegin());
SingleLinkedList<int>::ConstIterator const_it1;
// Присваивание ConstIterator'у значения Iterator
const_it1 = list.begin();
assert(const_it1 == const_it);
}
// Проверка оператора ->
{
using namespace std;
SingleLinkedList<std::string> string_list;
string_list.PushFront("one"s);
assert(string_list.cbegin()->length() == 3u);
string_list.begin()->push_back('!');
assert(*string_list.begin() == "one!"s);
}
}
void Test3() {
// Проверка списков на равенство и неравенство
{
SingleLinkedList<int> list_1;
list_1.PushFront(1);
list_1.PushFront(2);
SingleLinkedList<int> list_2;
list_2.PushFront(1);
list_2.PushFront(2);
list_2.PushFront(3);
SingleLinkedList<int> list_1_copy;
list_1_copy.PushFront(1);
list_1_copy.PushFront(2);
SingleLinkedList<int> empty_list;
SingleLinkedList<int> another_empty_list;
// Список равен самому себе
assert(list_1 == list_1);
assert(empty_list == empty_list);
// Списки с одинаковым содержимым равны, а с разным - не равны
assert(list_1 == list_1_copy);
assert(list_1 != list_2);
assert(list_2 != list_1);
assert(empty_list == another_empty_list);
}
// Обмен содержимого списков
{
SingleLinkedList<int> first;
first.PushFront(1);
first.PushFront(2);
SingleLinkedList<int> second;
second.PushFront(10);
second.PushFront(11);
second.PushFront(15);
const auto old_first_begin = first.begin();
const auto old_second_begin = second.begin();
const auto old_first_size = first.GetSize();
const auto old_second_size = second.GetSize();
first.swap(second);
assert(second.begin() == old_first_begin);
assert(first.begin() == old_second_begin);
assert(second.GetSize() == old_first_size);
assert(first.GetSize() == old_second_size);
// Обмен при помощи функции swap
{
using std::swap;
// В отсутствие пользовательской перегрузки будет вызвана функция std::swap, которая
// выполнит обмен через создание временной копии
swap(first, second);
// Убеждаемся, что используется не std::swap, а пользовательская перегрузка
// Если бы обмен был выполнен с созданием временной копии,
// то итератор first.begin() не будет равен ранее сохранённому значению,
// так как копия будет хранить свои узлы по иным адресам
assert(first.begin() == old_first_begin);
assert(second.begin() == old_second_begin);
assert(first.GetSize() == old_first_size);
assert(second.GetSize() == old_second_size);
}
}
// Инициализация списка при помощи std::initializer_list
{
SingleLinkedList<int> list{1, 2, 3, 4, 5};
assert(list.GetSize() == 5);
assert(!list.IsEmpty());
assert(std::equal(list.begin(), list.end(), std::begin({1, 2, 3, 4, 5})));
}
// Лексикографическое сравнение списков
{
using IntList = SingleLinkedList<int>;
assert((IntList{1, 2, 3} < IntList{1, 2, 3, 1}));
assert((IntList{1, 2, 3} <= IntList{1, 2, 3}));
assert((IntList{1, 2, 4} > IntList{1, 2, 3}));
assert((IntList{1, 2, 3} >= IntList{1, 2, 3}));
}
// Копирование списков
{
const SingleLinkedList<int> empty_list{};
// Копирование пустого списка
{
auto list_copy(empty_list);
assert(list_copy.IsEmpty());
}
SingleLinkedList<int> non_empty_list{1, 2, 3, 4};
// Копирование непустого списка
{
auto list_copy(non_empty_list);
assert(non_empty_list.begin() != list_copy.begin());
assert(list_copy == non_empty_list);
}
}
// Присваивание списков
{
const SingleLinkedList<int> source_list{1, 2, 3, 4};
SingleLinkedList<int> receiver{5, 4, 3, 2, 1};
receiver = source_list;
assert(receiver.begin() != source_list.begin());
assert(receiver == source_list);
}
// Вспомогательный класс, бросающий исключение после создания N-копии
struct ThrowOnCopy {
ThrowOnCopy() = default;
explicit ThrowOnCopy(int& copy_counter) noexcept
: countdown_ptr(&copy_counter) {
}
ThrowOnCopy(const ThrowOnCopy& other)
: countdown_ptr(other.countdown_ptr) //
{
if (countdown_ptr) {
if (*countdown_ptr == 0) {
throw std::bad_alloc();
} else {
--(*countdown_ptr);
}
}
}
// Присваивание элементов этого типа не требуется
ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
// Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
// Как только обнулится, конструктор копирования выбросит исключение
int* countdown_ptr = nullptr;
};
// Безопасное присваивание списков
{
SingleLinkedList<ThrowOnCopy> src_list;
src_list.PushFront(ThrowOnCopy{});
src_list.PushFront(ThrowOnCopy{});
auto thrower = src_list.begin();
src_list.PushFront(ThrowOnCopy{});
int copy_counter = 0; // при первом же копировании будет выброшего исключение
thrower->countdown_ptr = &copy_counter;
SingleLinkedList<ThrowOnCopy> dst_list;
dst_list.PushFront(ThrowOnCopy{});
int dst_counter = 10;
dst_list.begin()->countdown_ptr = &dst_counter;
dst_list.PushFront(ThrowOnCopy{});
try {
dst_list = src_list;
// Ожидается исключение при присваивании
assert(false);
} catch (const std::bad_alloc&) {
// Проверяем, что состояние списка-приёмника не изменилось
// при выбрасывании исключений
assert(dst_list.GetSize() == 2);
auto it = dst_list.begin();
assert(it != dst_list.end());
assert(it->countdown_ptr == nullptr);
++it;
assert(it != dst_list.end());
assert(it->countdown_ptr == &dst_counter);
assert(dst_counter == 10);
} catch (...) {
// Других типов исключений не ожидается
assert(false);
}
}
}
// Эта функция проверяет работу класса SingleLinkedList
void Test4() {
struct DeletionSpy {
~DeletionSpy() {
if (deletion_counter_ptr) {
++(*deletion_counter_ptr);
}
}
int* deletion_counter_ptr = nullptr;
};
// Проверка PopFront
{
SingleLinkedList<int> numbers{3, 14, 15, 92, 6};
numbers.PopFront();
assert((numbers == SingleLinkedList<int>{14, 15, 92, 6}));
SingleLinkedList<DeletionSpy> list;
list.PushFront(DeletionSpy{});
int deletion_counter = 0;
list.begin()->deletion_counter_ptr = &deletion_counter;
assert(deletion_counter == 0);
list.PopFront();
assert(deletion_counter == 1);
}
// Доступ к позиции, предшествующей begin
{
SingleLinkedList<int> empty_list;
const auto& const_empty_list = empty_list;
assert(empty_list.before_begin() == empty_list.cbefore_begin());
assert(++empty_list.before_begin() == empty_list.begin());
assert(++empty_list.cbefore_begin() == const_empty_list.begin());
SingleLinkedList<int> numbers{1, 2, 3, 4};
const auto& const_numbers = numbers;
assert(numbers.before_begin() == numbers.cbefore_begin());
assert(++numbers.before_begin() == numbers.begin());
assert(++numbers.cbefore_begin() == const_numbers.begin());
}
// Вставка элемента после указанной позиции
{ // Вставка в пустой список
{
SingleLinkedList<int> lst;
const auto inserted_item_pos = lst.InsertAfter(lst.before_begin(), 123);
assert((lst == SingleLinkedList<int>{123}));
assert(inserted_item_pos == lst.begin());
assert(*inserted_item_pos == 123);
}
// Вставка в непустой список
{
SingleLinkedList<int> lst{1, 2, 3};
auto inserted_item_pos = lst.InsertAfter(lst.before_begin(), 123);
assert(inserted_item_pos == lst.begin());
assert(inserted_item_pos != lst.end());
assert(*inserted_item_pos == 123);
assert((lst == SingleLinkedList<int>{123, 1, 2, 3}));
inserted_item_pos = lst.InsertAfter(lst.begin(), 555);
assert(++SingleLinkedList<int>::Iterator(lst.begin()) == inserted_item_pos);
assert(*inserted_item_pos == 555);
assert((lst == SingleLinkedList<int>{123, 555, 1, 2, 3}));
};
}
// Вспомогательный класс, бросающий исключение после создания N-копии
struct ThrowOnCopy {
ThrowOnCopy() = default;
explicit ThrowOnCopy(int& copy_counter) noexcept
: countdown_ptr(&copy_counter) {
}
ThrowOnCopy(const ThrowOnCopy& other)
: countdown_ptr(other.countdown_ptr) //
{
if (countdown_ptr) {
if (*countdown_ptr == 0) {
throw std::bad_alloc();
} else {
--(*countdown_ptr);
}
}
}
// Присваивание элементов этого типа не требуется
ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
// Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
// Как только обнулится, конструктор копирования выбросит исключение
int* countdown_ptr = nullptr;
};
// Проверка обеспечения строгой гарантии безопасности исключений
{
bool exception_was_thrown = false;
for (int max_copy_counter = 10; max_copy_counter >= 0; --max_copy_counter) {
SingleLinkedList<ThrowOnCopy> list{ThrowOnCopy{}, ThrowOnCopy{}, ThrowOnCopy{}};
try {
int copy_counter = max_copy_counter;
list.InsertAfter(list.cbegin(), ThrowOnCopy(copy_counter));
assert(list.GetSize() == 4u);
} catch (const std::bad_alloc&) {
exception_was_thrown = true;
assert(list.GetSize() == 3u);
break;
}
}
assert(exception_was_thrown);
}
// Удаление элементов после указанной позиции
{
{
SingleLinkedList<int> lst{1, 2, 3, 4};
const auto& const_lst = lst;
const auto item_after_erased = lst.EraseAfter(const_lst.cbefore_begin());
assert((lst == SingleLinkedList<int>{2, 3, 4}));
assert(item_after_erased == lst.begin());
}
{
SingleLinkedList<int> lst{1, 2, 3, 4};
const auto item_after_erased = lst.EraseAfter(lst.cbegin());
assert((lst == SingleLinkedList<int>{1, 3, 4}));
assert(item_after_erased == (++lst.begin()));
}
{
SingleLinkedList<int> lst{1, 2, 3, 4};
const auto item_after_erased = lst.EraseAfter(++(++lst.cbegin()));
assert((lst == SingleLinkedList<int>{1, 2, 3}));
assert(item_after_erased == lst.end());
}
{
SingleLinkedList<DeletionSpy> list{DeletionSpy{}, DeletionSpy{}, DeletionSpy{}};
auto after_begin = ++list.begin();
int deletion_counter = 0;
after_begin->deletion_counter_ptr = &deletion_counter;
assert(deletion_counter == 0u);
list.EraseAfter(list.cbegin());
assert(deletion_counter == 1u);
}
}
}
void TestRun(){
Test();
Test0();
Test1();
Test2();
Test3();
Test4();
}
int main() {
TestRun();
}
#ifndef SINGLELINKEDLIST_H
#define SINGLELINKEDLIST_H
#include <cassert>
#include <cstddef>
#include <string>
#include <utility>
#include <iostream>
using namespace std;
template <typename Type>
class SingleLinkedList {
// Узел списка
struct Node {
Node() = default;
Node(const Type& val, Node* next)
: value(val)
, next_node(next) {
}
Type value;
Node* next_node = nullptr;
};
template <typename ValueType>
class BasicIterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = Type;
using difference_type = std::ptrdiff_t;
using pointer = ValueType*;
using reference = ValueType&;
BasicIterator() = default;
BasicIterator(const BasicIterator<Type>& other) noexcept : node_(other.node_) {}
BasicIterator& operator=(const BasicIterator& other) = default;
[[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
return this->node_ == rhs.node_;
}
[[nodiscard]] bool operator!=(const BasicIterator<const Type>& rhs) const noexcept {
return !(*this == rhs);
}
[[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
return this->node_ == rhs.node_;
}
[[nodiscard]] bool operator!=(const BasicIterator<Type>& rhs) const noexcept {
return !(*this == rhs);
}
BasicIterator& operator++() noexcept {
assert(this->node_ != nullptr);
this->node_ = this->node_->next_node;
return *this;
}
BasicIterator operator++(int) noexcept {
auto old_value(*this);
++(*this);
return old_value;
}
[[nodiscard]] reference operator*() const noexcept {
assert(this->node_ != nullptr);
return node_->value;
}
[[nodiscard]] pointer operator->() const noexcept {
assert(this->node_ != nullptr);
return &node_->value ;
}
private:
// Разрешаем SingleLinkedList обращаться к приватной области
friend class SingleLinkedList;
explicit BasicIterator(Node* node): node_(node) {}
Node* node_ = nullptr;
};
public:
void swap(SingleLinkedList& other) noexcept {
std::swap(this->head_.next_node , other.head_.next_node);
std::swap(this->size_, other.size_);
}
SingleLinkedList() {};
SingleLinkedList(std::initializer_list<Type> values) {
Assign(values.begin(), values.end());
}
SingleLinkedList(const SingleLinkedList& other) {
Assign(other.begin(), other.end());
}
SingleLinkedList& operator=(const SingleLinkedList& rhs) {
auto rhs_copy(rhs);
this->swap(rhs_copy);
return *this;
}
void PushFront(const Type& value){
head_.next_node = new Node(value, head_.next_node);
++size_;
}
void Clear(){
while(!this->IsEmpty()){
auto temporary = head_.next_node->next_node;
delete head_.next_node;
head_.next_node = temporary;
--size_;
}
}
// Возвращает количество элементов в списке за время O(1)
[[nodiscard]] size_t GetSize() const noexcept {
// Заглушка. Реализуйте метод самостоятельно
return size_;
}
// Сообщает, пустой ли список за время O(1)
[[nodiscard]] bool IsEmpty() const noexcept {
// Заглушка. Реализуйте метод самостоятельно
return size_ == 0;
}
~SingleLinkedList(){
Clear();
}
using Iterator = BasicIterator<Type>;
using ConstIterator = BasicIterator<const Type>;
[[nodiscard]] Iterator begin() noexcept {
return Iterator{head_.next_node};
}
[[nodiscard]] Iterator end() noexcept {
return Iterator(nullptr);
}
// Константные версии begin/end для обхода списка без возможности модификации его элементов
[[nodiscard]] ConstIterator begin() const noexcept {
return ConstIterator{head_.next_node};
}
[[nodiscard]] ConstIterator end() const noexcept {
return ConstIterator(nullptr);
}
// Методы для удобного получения константных итераторов у неконстантного контейнера
[[nodiscard]] ConstIterator cbegin() const noexcept {
return ConstIterator{head_.next_node};
}
[[nodiscard]] ConstIterator cend() const noexcept {
return ConstIterator(nullptr);
}
[[nodiscard]] Iterator before_begin() noexcept{
return Iterator{&head_};
}
[[nodiscard]] ConstIterator cbefore_begin() const noexcept{
return ConstIterator{const_cast<Node *>(&head_)};
}
[[nodiscard]] ConstIterator before_begin() const noexcept{
return ConstIterator{&head_};
}
Iterator InsertAfter(ConstIterator pos, const Type& value) {
assert(pos.node_ != nullptr); // проверка на pos.node_->next_node != nullptr не нужна
auto next_copy(pos.node_->next_node);
pos.node_->next_node = new Node(value, next_copy);
pos.node_->next_node->next_node = next_copy;
++size_;
return Iterator{ pos.node_->next_node};
}
void PopFront() noexcept {
if(!IsEmpty()){
auto next_copy(head_.next_node->next_node);
delete head_.next_node;
head_.next_node = next_copy;
--size_;
}
}
Iterator EraseAfter(ConstIterator pos) noexcept {
assert(pos.node_ != nullptr && pos.node_->next_node != nullptr);
if(!IsEmpty()){ // понимаю , что this-> не обязателен.
auto next_copy(pos.node_->next_node->next_node);
delete pos.node_->next_node;
pos.node_->next_node = next_copy;
--size_;
}
return Iterator{pos.node_->next_node};
}
private:
template <typename InputIterator>
void Assign(InputIterator from, InputIterator to) {
SingleLinkedList<Type> tmp;
Node** node_ptr = &tmp.head_.next_node;
while (from != to) {
// Ожидается, что текущий указатель - нулевой
assert(*node_ptr == nullptr);
*node_ptr = new Node(*from, nullptr);
++tmp.size_;
node_ptr = &((*node_ptr)->next_node);
++from;
}
swap(tmp);
}
Node head_;
size_t size_ = 0;
};
template <typename Type>
void swap(SingleLinkedList<Type>& lhs, SingleLinkedList<Type>& rhs) noexcept {
lhs.swap(rhs);
}
template <typename Type>
bool operator==(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
if(&lhs == &rhs){
return true;
}
if(lhs.GetSize() != rhs.GetSize()){
return false;
}
bool equal = std::equal(lhs.begin(), lhs.end(), rhs.begin());
return equal;
}
template <typename Type>
bool operator!=(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
return !(lhs == rhs);
}
template <typename Type>
bool operator<(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
template <typename Type>
bool operator<=(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
return !(lhs > rhs);
}
template <typename Type>
bool operator>(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
return rhs < lhs;
}
template <typename Type>
bool operator>=(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
return !(lhs < rhs);
}
#endif // SINGLELINKEDLIST_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment