Advertisement
chevengur

СПРИНТ № 7 | Односвязный список | Урок 5: Сравнение, копирование и присваивание

May 7th, 2024
691
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.51 KB | None | 0 0
  1. #include <cassert>
  2. #include <cstddef>
  3. #include <string>
  4. #include <utility>
  5. #include <iostream>
  6. #include <vector>
  7. #include <algorithm>
  8.  
  9. template <typename Type>
  10. class SingleLinkedList {
  11.     // Узел списка
  12.     struct Node {
  13.         Node() = default;
  14.         Node(const Type& val, Node* next)
  15.             : value(val)
  16.             , next_node(next) {
  17.         }
  18.         Type value;
  19.         Node* next_node = nullptr;
  20.     };
  21.  
  22.  
  23.     template <typename ValueType>
  24.     class BasicIterator {
  25.         // Класс списка объявляется дружественным, чтобы из методов списка
  26.         // был доступ к приватной области итератора
  27.         friend class SingleLinkedList;
  28.  
  29.         // Конвертирующий конструктор итератора из указателя на узел списка
  30.         explicit BasicIterator(Node* node): node_(node) {
  31.         }
  32.  
  33.     public:
  34.         // Объявленные ниже типы сообщают стандартной библиотеке о свойствах этого итератора
  35.  
  36.         // Категория итератора — forward iterator
  37.         // (итератор, который поддерживает операции инкремента и многократное разыменование)
  38.         using iterator_category = std::forward_iterator_tag;
  39.         // Тип элементов, по которым перемещается итератор
  40.         using value_type = Type;
  41.         // Тип, используемый для хранения смещения между итераторами
  42.         using difference_type = std::ptrdiff_t;
  43.         // Тип указателя на итерируемое значение
  44.         using pointer = ValueType*;
  45.         // Тип ссылки на итерируемое значение
  46.         using reference = ValueType&;
  47.  
  48.         BasicIterator() = default;
  49.  
  50.         BasicIterator(const BasicIterator<Type>& other) noexcept {
  51.             node_ = other.node_;
  52.         }
  53.  
  54.         BasicIterator& operator=(const BasicIterator& rhs) = default;
  55.  
  56.         [[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
  57.             return node_ == rhs.node_;
  58.         }
  59.  
  60.         [[nodiscard]] bool operator!=(const BasicIterator<const Type>& rhs) const noexcept {
  61.             return !(node_ == rhs.node_);
  62.  
  63.         }
  64.  
  65.         [[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
  66.             return node_ == rhs.node_;
  67.         }
  68.  
  69.         [[nodiscard]] bool operator!=(const BasicIterator<Type>& rhs) const noexcept {
  70.             return !(node_ == rhs.node_);
  71.         }
  72.  
  73.         BasicIterator& operator++() noexcept {
  74.             node_ = node_->next_node;
  75.             return *this;
  76.         }
  77.  
  78.         BasicIterator operator++(int) noexcept {
  79.             auto old_value(*this);
  80.             ++(*this);
  81.             return old_value;
  82.         }
  83.  
  84.         [[nodiscard]] reference operator*() const noexcept {
  85.             return node_->value;
  86.         }
  87.  
  88.         [[nodiscard]] pointer operator->() const noexcept {
  89.             return &node_->value;
  90.         }
  91.  
  92.     private:
  93.         Node* node_ = nullptr;
  94.     };
  95.  
  96. public:
  97.  
  98.     using value_type = Type;
  99.     using reference = value_type&;
  100.     using const_reference = const value_type&;
  101.  
  102.     SingleLinkedList() : head_(), size_(0) {};
  103.  
  104.     ~SingleLinkedList() { Clear(); };
  105.  
  106.     template<typename iterator>
  107.     void Initialization(iterator begin, iterator end)
  108.     {
  109.         Node* newnode = &head_;
  110.         for (auto i = begin; i != end; ++i)
  111.         {
  112.             ++size_;
  113.             newnode->next_node = new Node(*i, nullptr);
  114.             newnode = newnode->next_node;
  115.         }
  116.     }
  117.  
  118.     SingleLinkedList(std::initializer_list<Type> values)
  119.     {
  120.         SingleLinkedList currentlist;
  121.         currentlist.Initialization(values.begin(), values.end());
  122.         swap(currentlist);
  123.     }
  124.  
  125.     SingleLinkedList(const SingleLinkedList& other)
  126.     {
  127.         SingleLinkedList currentlist;
  128.         currentlist.Initialization(other.begin(), other.end());
  129.         swap(currentlist);
  130.     }
  131.  
  132.     SingleLinkedList& operator=(const SingleLinkedList& rhs) {
  133.         if (this != &rhs)
  134.         {
  135.             SingleLinkedList currentlist(rhs);
  136.             swap(currentlist);
  137.         }
  138.         return *this;    
  139.     }
  140.  
  141.     // Обменивает содержимое списков за время O(1)
  142.     void swap(SingleLinkedList& other) noexcept {
  143.         std::swap(head_.next_node, other.head_.next_node);
  144.         std::swap(size_, other.size_);
  145.     }
  146.  
  147.     void PushFront(const Type& value) {
  148.         head_.next_node = new Node(value, head_.next_node);
  149.         ++size_;
  150.     }
  151.  
  152.     void Clear() noexcept {
  153.         while (head_.next_node)
  154.         {
  155.             Node* new_node = head_.next_node->next_node;
  156.             delete head_.next_node;
  157.             head_.next_node = new_node;
  158.         }
  159.         size_ = 0;
  160.     }
  161.  
  162.     [[nodiscard]] size_t GetSize() const noexcept {
  163.         return size_;
  164.     }
  165.  
  166.     [[nodiscard]] bool IsEmpty() const noexcept {
  167.         return (size_ == 0) ? true : false;
  168.     }
  169.  
  170.     // Итератор, допускающий изменение элементов списка
  171.     using Iterator = BasicIterator<Type>;
  172.     // Константный итератор, предоставляющий доступ для чтения к элементам списка
  173.     using ConstIterator = BasicIterator<const Type>;
  174.  
  175.     [[nodiscard]] Iterator begin() noexcept {
  176.         return Iterator{ head_.next_node };
  177.     }
  178.  
  179.     [[nodiscard]] Iterator end() noexcept {
  180.         return Iterator{ nullptr };
  181.     }
  182.  
  183.     [[nodiscard]] ConstIterator begin() const noexcept {
  184.         return ConstIterator{ head_.next_node };
  185.     }
  186.  
  187.     [[nodiscard]] ConstIterator end() const noexcept {
  188.         return ConstIterator{ nullptr };
  189.     }
  190.  
  191.     [[nodiscard]] ConstIterator cbegin() const noexcept {
  192.         return ConstIterator{ head_.next_node };
  193.     }
  194.  
  195.     [[nodiscard]] ConstIterator cend() const noexcept {
  196.         return ConstIterator{ nullptr };
  197.     }
  198.  
  199. private:
  200.     Node head_;
  201.     size_t size_;
  202. };
  203.  
  204. template <typename Type>
  205. void swap(SingleLinkedList<Type>& lhs, SingleLinkedList<Type>& rhs) noexcept {
  206.     lhs.swap(rhs);
  207. }
  208.  
  209. template <typename Type>
  210. bool operator==(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
  211.     return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
  212. }
  213.  
  214. template <typename Type>
  215. bool operator!=(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
  216.     return !(lhs == rhs);
  217. }
  218.  
  219. template <typename Type>
  220. bool operator<(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
  221.     return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
  222. }
  223.  
  224. template <typename Type>
  225. bool operator<=(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
  226.     return (lhs < rhs) || (lhs == rhs);
  227. }
  228.  
  229. template <typename Type>
  230. bool operator>(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
  231.     return !(lhs < rhs);
  232. }
  233.  
  234. template <typename Type>
  235. bool operator>=(const SingleLinkedList<Type>& lhs, const SingleLinkedList<Type>& rhs) {
  236.     return (lhs > rhs) || (lhs == rhs);
  237. }
  238.  
  239. // Эта функция проверяет работу класса SingleLinkedList
  240. void Test3() {
  241.     // Проверка списков на равенство и неравенство
  242.     {
  243.         SingleLinkedList<int> list_1;
  244.         list_1.PushFront(1);
  245.         list_1.PushFront(2);
  246.  
  247.         SingleLinkedList<int> list_2;
  248.         list_2.PushFront(1);
  249.         list_2.PushFront(2);
  250.         list_2.PushFront(3);
  251.  
  252.         SingleLinkedList<int> list_1_copy;
  253.         list_1_copy.PushFront(1);
  254.         list_1_copy.PushFront(2);
  255.  
  256.         SingleLinkedList<int> empty_list;
  257.         SingleLinkedList<int> another_empty_list;
  258.  
  259.         // Список равен самому себе
  260.         assert(list_1 == list_1);
  261.         assert(empty_list == empty_list);
  262.  
  263.         // Списки с одинаковым содержимым равны, а с разным - не равны
  264.         assert(list_1 == list_1_copy);
  265.         assert(list_1 != list_2);
  266.         assert(list_2 != list_1);
  267.         assert(empty_list == another_empty_list);
  268.     }
  269.  
  270.     // Обмен содержимого списков
  271.     {
  272.         SingleLinkedList<int> first;
  273.         first.PushFront(1);
  274.         first.PushFront(2);
  275.  
  276.         SingleLinkedList<int> second;
  277.         second.PushFront(10);
  278.         second.PushFront(11);
  279.         second.PushFront(15);
  280.  
  281.         const auto old_first_begin = first.begin();
  282.         const auto old_second_begin = second.begin();
  283.         const auto old_first_size = first.GetSize();
  284.         const auto old_second_size = second.GetSize();
  285.  
  286.         first.swap(second);
  287.  
  288.         assert(second.begin() == old_first_begin);
  289.         assert(first.begin() == old_second_begin);
  290.         assert(second.GetSize() == old_first_size);
  291.         assert(first.GetSize() == old_second_size);
  292.  
  293.         // Обмен при помощи функции swap
  294.         {
  295.             using std::swap;
  296.  
  297.             // В отсутствие пользовательской перегрузки будет вызвана функция std::swap, которая
  298.             // выполнит обмен через создание временной копии
  299.             swap(first, second);
  300.  
  301.             // Убеждаемся, что используется не std::swap, а пользовательская перегрузка
  302.  
  303.             // Если бы обмен был выполнен с созданием временной копии,
  304.             // то итератор first.begin() не будет равен ранее сохранённому значению,
  305.             // так как копия будет хранить свои узлы по иным адресам
  306.             assert(first.begin() == old_first_begin);
  307.             assert(second.begin() == old_second_begin);
  308.             assert(first.GetSize() == old_first_size);
  309.             assert(second.GetSize() == old_second_size);
  310.         }
  311.     }
  312.  
  313.     // Инициализация списка при помощи std::initializer_list
  314.     {
  315.         SingleLinkedList<int> list{ 1, 2, 3, 4, 5 };
  316.         assert(list.GetSize() == 5);
  317.         assert(!list.IsEmpty());
  318.         assert(std::equal(list.begin(), list.end(), std::begin({ 1, 2, 3, 4, 5 })));
  319.     }
  320.  
  321.     // Лексикографическое сравнение списков
  322.     {
  323.         using IntList = SingleLinkedList<int>;
  324.  
  325.         assert((IntList{ 1, 2, 3 } < IntList{ 1, 2, 3, 1 }));
  326.         assert((IntList{ 1, 2, 3 } <= IntList{ 1, 2, 3 }));
  327.         assert((IntList{ 1, 2, 4 } > IntList{ 1, 2, 3 }));
  328.         assert((IntList{ 1, 2, 3 } >= IntList{ 1, 2, 3 }));
  329.     }
  330.  
  331.     // Копирование списков
  332.     {
  333.         const SingleLinkedList<int> empty_list{};
  334.         // Копирование пустого списка
  335.         {
  336.             auto list_copy(empty_list);
  337.             assert(list_copy.IsEmpty());
  338.         }
  339.  
  340.         SingleLinkedList<int> non_empty_list{ 1, 2, 3, 4 };
  341.         // Копирование непустого списка
  342.         {
  343.             auto list_copy(non_empty_list);
  344.  
  345.             assert(non_empty_list.begin() != list_copy.begin());
  346.             assert(list_copy == non_empty_list);
  347.         }
  348.     }
  349.  
  350.     // Присваивание списков
  351.     {
  352.         const SingleLinkedList<int> source_list{ 1, 2, 3, 4 };
  353.  
  354.         SingleLinkedList<int> receiver{ 5, 4, 3, 2, 1 };
  355.         receiver = source_list;
  356.         assert(receiver.begin() != source_list.begin());
  357.         assert(receiver == source_list);
  358.     }
  359.  
  360.     // Вспомогательный класс, бросающий исключение после создания N-копии
  361.     struct ThrowOnCopy {
  362.         ThrowOnCopy() = default;
  363.         explicit ThrowOnCopy(int& copy_counter) noexcept
  364.             : countdown_ptr(&copy_counter) {
  365.         }
  366.         ThrowOnCopy(const ThrowOnCopy& other)
  367.             : countdown_ptr(other.countdown_ptr)  //
  368.         {
  369.             if (countdown_ptr) {
  370.                 if (*countdown_ptr == 0) {
  371.                     throw std::bad_alloc();
  372.                 }
  373.                 else {
  374.                     --(*countdown_ptr);
  375.                 }
  376.             }
  377.         }
  378.         // Присваивание элементов этого типа не требуется
  379.         ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
  380.         // Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
  381.         // Как только обнулится, конструктор копирования выбросит исключение
  382.         int* countdown_ptr = nullptr;
  383.     };
  384.  
  385.     // Безопасное присваивание списков
  386.     {
  387.         SingleLinkedList<ThrowOnCopy> src_list;
  388.         src_list.PushFront(ThrowOnCopy{});
  389.         src_list.PushFront(ThrowOnCopy{});
  390.         auto thrower = src_list.begin();
  391.         src_list.PushFront(ThrowOnCopy{});
  392.  
  393.         int copy_counter = 0;  // при первом же копировании будет выброшего исключение
  394.         thrower->countdown_ptr = &copy_counter;
  395.  
  396.         SingleLinkedList<ThrowOnCopy> dst_list;
  397.         dst_list.PushFront(ThrowOnCopy{});
  398.         int dst_counter = 10;
  399.         dst_list.begin()->countdown_ptr = &dst_counter;
  400.         dst_list.PushFront(ThrowOnCopy{});
  401.  
  402.         try {
  403.             dst_list = src_list;
  404.             // Ожидается исключение при присваивании
  405.             assert(false);
  406.         }
  407.         catch (const std::bad_alloc&) {
  408.             // Проверяем, что состояние списка-приёмника не изменилось
  409.             // при выбрасывании исключений
  410.             assert(dst_list.GetSize() == 2);
  411.             auto it = dst_list.begin();
  412.             assert(it != dst_list.end());
  413.             assert(it->countdown_ptr == nullptr);
  414.             ++it;
  415.             assert(it != dst_list.end());
  416.             assert(it->countdown_ptr == &dst_counter);
  417.             assert(dst_counter == 10);
  418.         }
  419.         catch (...) {
  420.             // Других типов исключений не ожидается
  421.             assert(false);
  422.         }
  423.     }
  424. }
  425.  
  426. int main() {
  427.     Test3();
  428. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement