Advertisement
chevengur

СПРИНТ № 7 | Односвязный список | Урок 3: Вставка элементов и очистка списка

Apr 30th, 2024
935
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.64 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. public:
  20.  
  21.     SingleLinkedList() : head_(), size_(0) {};
  22.  
  23.     ~SingleLinkedList() { Clear(); };
  24.  
  25.     // Вставляет элемент value в начало списка за время O(1)
  26.     void PushFront(const Type& value) {
  27.         head_.next_node = new Node(value, head_.next_node);
  28.         ++size_;
  29.     }
  30.  
  31.     // Очищает список за время O(N)
  32.     void Clear() noexcept {
  33.         while (head_.next_node)
  34.         {
  35.             Node* new_node = head_.next_node->next_node;
  36.             delete head_.next_node;
  37.             head_.next_node = new_node;
  38.         }
  39.         size_ = 0;
  40.     }
  41.  
  42.     // Возвращает количество элементов в списке за время O(1)
  43.     [[nodiscard]] size_t GetSize() const noexcept {
  44.         return size_;
  45.     }
  46.  
  47.     // Сообщает, пустой ли список за время O(1)
  48.     [[nodiscard]] bool IsEmpty() const noexcept {
  49.         return (size_ == 0) ? true : false;
  50.     }
  51.  
  52. private:
  53.     // Фиктивный узел, используется для вставки "перед первым элементом"
  54.     Node head_;
  55.     size_t size_;
  56. };
  57.  
  58. // Эта функция тестирует работу SingleLinkedList
  59. void Test1() {
  60.     // Шпион, следящий за своим удалением
  61.     struct DeletionSpy {
  62.         DeletionSpy() = default;
  63.         explicit DeletionSpy(int& instance_counter) noexcept
  64.             : instance_counter_ptr_(&instance_counter)  //
  65.         {
  66.             OnAddInstance();
  67.         }
  68.         DeletionSpy(const DeletionSpy& other) noexcept
  69.             : instance_counter_ptr_(other.instance_counter_ptr_)  //
  70.         {
  71.             OnAddInstance();
  72.         }
  73.         DeletionSpy& operator=(const DeletionSpy& rhs) noexcept {
  74.             if (this != &rhs) {
  75.                 auto rhs_copy(rhs);
  76.                 std::swap(instance_counter_ptr_, rhs_copy.instance_counter_ptr_);
  77.             }
  78.             return *this;
  79.         }
  80.         ~DeletionSpy() {
  81.             OnDeleteInstance();
  82.         }
  83.  
  84.     private:
  85.         void OnAddInstance() noexcept {
  86.             if (instance_counter_ptr_) {
  87.                 ++(*instance_counter_ptr_);
  88.             }
  89.         }
  90.         void OnDeleteInstance() noexcept {
  91.             if (instance_counter_ptr_) {
  92.                 assert(*instance_counter_ptr_ != 0);
  93.                 --(*instance_counter_ptr_);
  94.             }
  95.         }
  96.  
  97.         int* instance_counter_ptr_ = nullptr;
  98.     };
  99.  
  100.     // Проверка вставки в начало
  101.     {
  102.         SingleLinkedList<int> l;
  103.         assert(l.IsEmpty());
  104.         assert(l.GetSize() == 0u);
  105.  
  106.         l.PushFront(0);
  107.         l.PushFront(1);
  108.         assert(l.GetSize() == 2);
  109.         assert(!l.IsEmpty());
  110.  
  111.         l.Clear();
  112.         assert(l.GetSize() == 0);
  113.         assert(l.IsEmpty());
  114.     }
  115.  
  116.     // Проверка фактического удаления элементов
  117.     {
  118.         int item0_counter = 0;
  119.         int item1_counter = 0;
  120.         int item2_counter = 0;
  121.         {
  122.             SingleLinkedList<DeletionSpy> list;
  123.             list.PushFront(DeletionSpy{ item0_counter });
  124.             list.PushFront(DeletionSpy{ item1_counter });
  125.             list.PushFront(DeletionSpy{ item2_counter });
  126.  
  127.             assert(item0_counter == 1);
  128.             assert(item1_counter == 1);
  129.             assert(item2_counter == 1);
  130.             list.Clear();
  131.             assert(item0_counter == 0);
  132.             assert(item1_counter == 0);
  133.             assert(item2_counter == 0);
  134.  
  135.             list.PushFront(DeletionSpy{ item0_counter });
  136.             list.PushFront(DeletionSpy{ item1_counter });
  137.             list.PushFront(DeletionSpy{ item2_counter });
  138.             assert(item0_counter == 1);
  139.             assert(item1_counter == 1);
  140.             assert(item2_counter == 1);
  141.         }
  142.         assert(item0_counter == 0);
  143.         assert(item1_counter == 0);
  144.         assert(item2_counter == 0);
  145.     }
  146.  
  147.     // Вспомогательный класс, бросающий исключение после создания N-копии
  148.     struct ThrowOnCopy {
  149.         ThrowOnCopy() = default;
  150.         explicit ThrowOnCopy(int& copy_counter) noexcept
  151.             : countdown_ptr(&copy_counter) {
  152.         }
  153.         ThrowOnCopy(const ThrowOnCopy& other)
  154.             : countdown_ptr(other.countdown_ptr)  //
  155.         {
  156.             if (countdown_ptr) {
  157.                 if (*countdown_ptr == 0) {
  158.                     throw std::bad_alloc();
  159.                 }
  160.                 else {
  161.                     --(*countdown_ptr);
  162.                 }
  163.             }
  164.         }
  165.         // Присваивание элементов этого типа не требуется
  166.         ThrowOnCopy& operator=(const ThrowOnCopy& rhs) = delete;
  167.         // Адрес счётчика обратного отсчёта. Если не равен nullptr, то уменьшается при каждом копировании.
  168.         // Как только обнулится, конструктор копирования выбросит исключение
  169.         int* countdown_ptr = nullptr;
  170.     };
  171.  
  172.     {
  173.         bool exception_was_thrown = false;
  174.         // Последовательно уменьшаем счётчик копирований до нуля, пока не будет выброшено исключение
  175.         for (int max_copy_counter = 5; max_copy_counter >= 0; --max_copy_counter) {
  176.             // Создаём непустой список
  177.             SingleLinkedList<ThrowOnCopy> list;
  178.             list.PushFront(ThrowOnCopy{});
  179.             try {
  180.                 int copy_counter = max_copy_counter;
  181.                 list.PushFront(ThrowOnCopy(copy_counter));
  182.                 // Если метод не выбросил исключение, список должен перейти в новое состояние
  183.                 assert(list.GetSize() == 2);
  184.             }
  185.             catch (const std::bad_alloc&) {
  186.                 exception_was_thrown = true;
  187.                 // После выбрасывания исключения состояние списка должно остаться прежним
  188.                 assert(list.GetSize() == 1);
  189.                 break;
  190.             }
  191.         }
  192.         assert(exception_was_thrown);
  193.     }
  194. }
  195.  
  196. int main() {
  197.     Test1();
  198. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement