shizaero

Untitled

Aug 12th, 2023
976
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.25 KB | None | 0 0
  1. #include <cassert>
  2. #include <cstddef>
  3. #include <string>
  4. #include <utility>
  5.  
  6. template <typename Type>
  7. class SingleLinkedList {
  8.     // Узел списка
  9.     struct Node {
  10.         Node() = default;
  11.         Node(const Type& val, Node* next)
  12.             : value(val)
  13.             , next_node(next) {
  14.         }
  15.         Type value;
  16.         Node* next_node = nullptr;
  17.     };
  18.  
  19.     template <typename ValueType>
  20.     class BasicIterator {
  21.         // Класс списка объявляется дружественным, чтобы из методов списка
  22.         // был доступ к приватной области итератора
  23.         friend class SingleLinkedList;
  24.  
  25.         // Конвертирующий конструктор итератора из указателя на узел списка
  26.         explicit BasicIterator(Node* node)
  27.             :node_(node)
  28.         {
  29.         }
  30.  
  31.     public:
  32.         // Объявленные ниже типы сообщают стандартной библиотеке о свойствах этого итератора
  33.  
  34.         // Категория итератора — forward iterator
  35.         // (итератор, который поддерживает операции инкремента и многократное разыменование)
  36.         using iterator_category = std::forward_iterator_tag;
  37.  
  38.         // Тип элементов, по которым перемещается итератор
  39.         using value_type = Type;
  40.  
  41.         // Тип, используемый для хранения смещения между итераторами
  42.         using difference_type = std::ptrdiff_t;
  43.  
  44.         // Тип указателя на итерируемое значение
  45.         using pointer = ValueType*;
  46.  
  47.         // Тип ссылки на итерируемое значение
  48.         using reference = ValueType&;
  49.  
  50.         BasicIterator() = default;
  51.  
  52.         // Конвертирующий конструктор/конструктор копирования
  53.         // При ValueType, совпадающем с Type, играет роль копирующего конструктора
  54.         // При ValueType, совпадающем с const Type, играет роль конвертирующего конструктора
  55.         BasicIterator(const BasicIterator<Type>& other) noexcept
  56.             : node_(other.node_)
  57.         {
  58.         }
  59.  
  60.         // Чтобы компилятор не выдавал предупреждение об отсутствии оператора = при наличии
  61.         // пользовательского конструктора копирования, явно объявим оператор = и
  62.         // попросим компилятор сгенерировать его за нас
  63.         BasicIterator& operator=(const BasicIterator& rhs) = default;
  64.  
  65.         // Оператор сравнения итераторов (в роли второго аргумента выступает константный итератор)
  66.         // Два итератора равны, если они ссылаются на один и тот же элемент списка либо на end()
  67.         [[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
  68.             return rhs.node_ == node_;
  69.         }
  70.  
  71.         // Оператор проверки итераторов на неравенство
  72.         // Противоположен !=
  73.         [[nodiscard]] bool operator!=(const BasicIterator<const Type>& rhs) const noexcept {
  74.             return rhs.node_ != node_;
  75.         }
  76.  
  77.         // Оператор сравнения итераторов (в роли второго аргумента итератор)
  78.         // Два итератора равны, если они ссылаются на один и тот же элемент списка либо на end()
  79.         [[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
  80.             return rhs.node_ == node_;
  81.         }
  82.  
  83.         // Оператор проверки итераторов на неравенство
  84.         // Противоположен !=
  85.         [[nodiscard]] bool operator!=(const BasicIterator<Type>& rhs) const noexcept {
  86.             return rhs.node_ != node_;
  87.         }
  88.  
  89.         // Оператор прединкремента. После его вызова итератор указывает на следующий элемент списка
  90.         // Возвращает ссылку на самого себя
  91.         // Инкремент итератора, не указывающего на существующий элемент списка, приводит к неопределённому поведению
  92.         BasicIterator& operator++() noexcept {
  93.             return *this;
  94.         }
  95.  
  96.         // Оператор постинкремента. После его вызова итератор указывает на следующий элемент списка
  97.         // Возвращает прежнее значение итератора
  98.         // Инкремент итератора, не указывающего на существующий элемент списка,
  99.         // приводит к неопределённому поведению
  100.         BasicIterator operator++(int) noexcept {
  101.             auto old_value(*this); // Сохраняем прежнее значение объекта для последующего возврата
  102.             ++(*this); // используем логику префиксной формы инкремента
  103.             return old_value;
  104.         }
  105.  
  106.         // Операция разыменования. Возвращает ссылку на текущий элемент
  107.         // Вызов этого оператора у итератора, не указывающего на существующий элемент списка,
  108.         // приводит к неопределённому поведению
  109.         [[nodiscard]] reference operator*() const noexcept {
  110.             return node_->value;
  111.         }
  112.  
  113.         // Операция доступа к члену класса. Возвращает указатель на текущий элемент списка
  114.         // Вызов этого оператора у итератора, не указывающего на существующий элемент списка,
  115.         // приводит к неопределённому поведению
  116.         [[nodiscard]] pointer operator->() const noexcept {
  117.             return &node_->value;
  118.         }
  119.  
  120.     private:
  121.         Node* node_ = nullptr;
  122.     };
  123.  
  124. public:
  125.     using value_type = Type;
  126.     using reference = value_type&;
  127.     using const_reference = const value_type&;
  128.  
  129.     // Итератор, допускающий изменение элементов списка
  130.     using Iterator = BasicIterator<Type>;
  131.  
  132.     // Константный итератор, предоставляющий доступ для чтения к элементам списка
  133.     using ConstIterator = BasicIterator<const Type>;
  134.  
  135.     // Возвращает итератор, ссылающийся на первый элемент
  136.     // Если список пустой, возвращённый итератор будет равен end()
  137.     [[nodiscard]] Iterator begin() noexcept {
  138.         return (size_ == 0) ? end() : BasicIterator<Type>(head_.next_node);
  139.     }
  140.  
  141.     // Возвращает итератор, указывающий на позицию, следующую за последним элементом односвязного списка
  142.     // Разыменовывать этот итератор нельзя — попытка разыменования приведёт к неопределённому поведению
  143.     [[nodiscard]] Iterator end() noexcept {
  144.         auto it = head_.next_node;
  145.         while (it != nullptr) {
  146.             ++it;
  147.         }
  148.         return BasicIterator<Type>(--it);
  149.     }
  150.  
  151.     // Возвращает константный итератор, ссылающийся на первый элемент
  152.     // Если список пустой, возвращённый итератор будет равен end()
  153.     // Результат вызова эквивалентен вызову метода cbegin()
  154.     [[nodiscard]] ConstIterator begin() const noexcept {
  155.         return (size_ == 0) ? end() : cbegin();
  156.     }
  157.  
  158.     // Возвращает константный итератор, указывающий на позицию, следующую за последним элементом односвязного списка
  159.     // Разыменовывать этот итератор нельзя — попытка разыменования приведёт к неопределённому поведению
  160.     // Результат вызова эквивалентен вызову метода cend()
  161.     [[nodiscard]] ConstIterator end() const noexcept {
  162.         auto it = head_.next_node;
  163.         while (it != nullptr) {
  164.             ++it;
  165.         }
  166.         return BasicIterator<Type>(--it);
  167.     }
  168.  
  169.     // Возвращает константный итератор, ссылающийся на первый элемент
  170.     // Если список пустой, возвращённый итератор будет равен cend()
  171.     [[nodiscard]] ConstIterator cbegin() const noexcept {
  172.         return size_ == 0 ? cend() : BasicIterator<Type>(head_.next_node);
  173.     }
  174.  
  175.     // Возвращает константный итератор, указывающий на позицию, следующую за последним элементом односвязного списка
  176.     // Разыменовывать этот итератор нельзя — попытка разыменования приведёт к неопределённому поведению
  177.     [[nodiscard]] ConstIterator cend() const noexcept {
  178.         return cend();
  179.     }
  180.  
  181.     SingleLinkedList() = default;
  182.  
  183.     void PushFront(const Type& value) {
  184.         head_.next_node = new Node(value, head_.next_node);
  185.         ++size_;
  186.     }
  187.  
  188.     void Clear() noexcept {
  189.         while (head_.next_node != nullptr) {
  190.             auto ptr_copy = head_.next_node;
  191.             head_.next_node = ptr_copy->next_node;
  192.             delete ptr_copy;
  193.         }
  194.         size_ = 0;
  195.     }
  196.  
  197.     // Возвращает количество элементов в списке за время O(1)
  198.     [[nodiscard]] size_t GetSize() const noexcept {
  199.         return size_;
  200.     }
  201.  
  202.     // Сообщает, пустой ли список за время O(1)
  203.     [[nodiscard]] bool IsEmpty() const noexcept {
  204.         return size_ != 0 ? false : true;
  205.     }
  206.     ~SingleLinkedList() {
  207.         Clear();
  208.     }
  209.  
  210. private:
  211.     // Фиктивный узел, используется для вставки "перед первым элементом"
  212.     Node head_;
  213.     size_t size_ = 0;
  214. };
  215.  
  216. void Test1() {
  217.     // Шпион, следящий за своим удалением
  218.     struct DeletionSpy {
  219.         DeletionSpy() = default;
  220.         explicit DeletionSpy(int& instance_counter) noexcept
  221.             : instance_counter_ptr_(&instance_counter)  //
  222.         {
  223.             OnAddInstance();
  224.         }
  225.         DeletionSpy(const DeletionSpy& other) noexcept
  226.             : instance_counter_ptr_(other.instance_counter_ptr_)  //
  227.         {
  228.             OnAddInstance();
  229.         }
  230.         DeletionSpy& operator=(const DeletionSpy& rhs) noexcept {
  231.             if (this != &rhs) {
  232.                 auto rhs_copy(rhs);
  233.                 std::swap(instance_counter_ptr_, rhs_copy.instance_counter_ptr_);
  234.             }
  235.             return *this;
  236.         }
  237.         ~DeletionSpy() {
  238.             OnDeleteInstance();
  239.         }
  240.  
  241.     private:
  242.         void OnAddInstance() noexcept {
  243.             if (instance_counter_ptr_) {
  244.                 ++(*instance_counter_ptr_);
  245.             }
  246.         }
  247.         void OnDeleteInstance() noexcept {
  248.             if (instance_counter_ptr_) {
  249.                 assert(*instance_counter_ptr_ != 0);
  250.                 --(*instance_counter_ptr_);
  251.             }
  252.         }
  253.  
  254.         int* instance_counter_ptr_ = nullptr;
  255.     };
  256.  
  257.     // Проверка вставки в начало
  258.     {
  259.         SingleLinkedList<int> l;
  260.         assert(l.IsEmpty());
  261.         assert(l.GetSize() == 0u);
  262.  
  263.         l.PushFront(0);
  264.         l.PushFront(1);
  265.         assert(l.GetSize() == 2);
  266.         assert(!l.IsEmpty());
  267.  
  268.         l.Clear();
  269.         assert(l.GetSize() == 0);
  270.         assert(l.IsEmpty());
  271.     }
  272.  
  273.     // Проверка фактического удаления элементов
  274.     {
  275.         int item0_counter = 0;
  276.         int item1_counter = 0;
  277.         int item2_counter = 0;
  278.         {
  279.             SingleLinkedList<DeletionSpy> list;
  280.             list.PushFront(DeletionSpy{ item0_counter });
  281.             list.PushFront(DeletionSpy{ item1_counter });
  282.             list.PushFront(DeletionSpy{ item2_counter });
  283.  
  284.             assert(item0_counter == 1);
  285.             assert(item1_counter == 1);
  286.             assert(item2_counter == 1);
  287.             list.Clear();
  288.             assert(item0_counter == 0);
  289.             assert(item1_counter == 0);
  290.             assert(item2_counter == 0);
  291.  
  292.             list.PushFront(DeletionSpy{ item0_counter });
  293.             list.PushFront(DeletionSpy{ item1_counter });
  294.             list.PushFront(DeletionSpy{ item2_counter });
  295.             assert(item0_counter == 1);
  296.             assert(item1_counter == 1);
  297.             assert(item2_counter == 1);
  298.         }
  299.         assert(item0_counter == 0);
  300.         assert(item1_counter == 0);
  301.         assert(item2_counter == 0);
  302.     }
  303.  
  304.     // Вспомогательный класс, бросающий исключение после создания N-копии
  305.     struct ThrowOnCopy {
  306.         ThrowOnCopy() = default;
  307.         explicit ThrowOnCopy(int& copy_counter) noexcept
  308.             : countdown_ptr(&copy_counter) {
  309.         }
  310.         ThrowOnCopy(const ThrowOnCopy& other)
  311.             : countdown_ptr(other.countdown_ptr)  //
  312.         {
  313.             if (countdown_ptr) {
  314.                 if (*countdown_ptr == 0) {
  315.                     throw std::bad_alloc();
  316.                 }
  317.                 else {
  318.                     --(*countdown_ptr);
  319.                 }
  320.             }
  321.         }
  322.         // Присваивание элементов этого типа не требуется
  323.         ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
  324.         // Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
  325.         // Как только обнулится, конструктор копирования выбросит исключение
  326.         int* countdown_ptr = nullptr;
  327.     };
  328.  
  329.     {
  330.         bool exception_was_thrown = false;
  331.         // Последовательно уменьшаем счётчик копирований до нуля, пока не будет выброшено исключение
  332.         for (int max_copy_counter = 5; max_copy_counter >= 0; --max_copy_counter) {
  333.             // Создаём непустой список
  334.             SingleLinkedList<ThrowOnCopy> list;
  335.             list.PushFront(ThrowOnCopy{});
  336.             try {
  337.                 int copy_counter = max_copy_counter;
  338.                 list.PushFront(ThrowOnCopy(copy_counter));
  339.                 // Если метод не выбросил исключение, список должен перейти в новое состояние
  340.                 assert(list.GetSize() == 2);
  341.             }
  342.             catch (const std::bad_alloc&) {
  343.                 exception_was_thrown = true;
  344.                 // После выбрасывания исключения состояние списка должно остаться прежним
  345.                 assert(list.GetSize() == 1);
  346.                 break;
  347.             }
  348.         }
  349.         assert(exception_was_thrown);
  350.     }
  351. }
  352.  
  353. int main() {
  354.     Test1();
  355. }
Advertisement
Add Comment
Please, Sign In to add comment