Advertisement
kutuzzzov

Урок 4 покоряем итераторы

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