Advertisement
AlexDanilin

С8. Урок 8 Move-семантика для вектора.(итог)

Aug 21st, 2023 (edited)
356
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 19.83 KB | None | 0 0
  1. /*-----------------------array.h--------------------*/
  2. #pragma once
  3.  
  4. #include <cassert>
  5. #include <cstdlib>
  6. #include <algorithm>
  7.  
  8. template <typename Type>
  9. class ArrayPtr {
  10. public:
  11.     // Инициализирует ArrayPtr нулевым указателем
  12.     ArrayPtr() = default;
  13.  
  14.     // Создаёт в куче массив из size элементов типа Type.
  15.     // Если size == 0, поле raw_ptr_ должно быть равно nullptr
  16.     explicit ArrayPtr(size_t size) {
  17.         if (size == 0) raw_ptr_ = nullptr;
  18.         else raw_ptr_ = new Type[size];
  19.     }
  20.  
  21.     // Конструктор из сырого указателя, хранящего адрес массива в куче либо nullptr
  22.     explicit ArrayPtr(Type* raw_ptr) noexcept
  23.         : raw_ptr_(raw_ptr)
  24.     {
  25.     }
  26.  
  27.     // Запрещаем копирование
  28.     ArrayPtr(const ArrayPtr&) = delete;
  29.  
  30.     ~ArrayPtr() {
  31.         delete[] raw_ptr_;
  32.     }
  33.  
  34.     // Запрещаем присваивание
  35.     ArrayPtr& operator=(const ArrayPtr&) = delete;
  36.  
  37.     // Прекращает владением массивом в памяти, возвращает значение адреса массива
  38.     // После вызова метода указатель на массив должен обнулиться
  39.     [[nodiscard]] Type* Release() noexcept {
  40.         Type* tmp = raw_ptr_;
  41.         raw_ptr_ = nullptr;
  42.         return tmp;
  43.     }
  44.  
  45.     // Возвращает ссылку на элемент массива с индексом index
  46.     Type& operator[](size_t index) noexcept {
  47.         return raw_ptr_[index];
  48.     }
  49.  
  50.     // Возвращает константную ссылку на элемент массива с индексом index
  51.     const Type& operator[](size_t index) const noexcept {
  52.         return raw_ptr_[index];
  53.     }
  54.  
  55.     // Возвращает true, если указатель ненулевой, и false в противном случае
  56.     explicit operator bool() const {
  57.         if (raw_ptr_) return true;
  58.         return false;
  59.     }
  60.  
  61.     // Возвращает значение сырого указателя, хранящего адрес начала массива
  62.     Type* Get() const noexcept {
  63.         return &raw_ptr_[0];
  64.     }
  65.  
  66.     // Обменивается значениям указателя на массив с объектом other
  67.     void swap(ArrayPtr& other) noexcept {
  68.         std::swap(other.raw_ptr_, raw_ptr_);
  69.     }
  70.  
  71. private:
  72.     Type* raw_ptr_ = nullptr;
  73. };
  74. /*------------------------------------------------------------------simple-vector.h----------------------------------*/
  75. #pragma once
  76.  
  77. #include "array_ptr.h"
  78.  
  79. #include <cassert>
  80. #include <initializer_list>
  81. #include <stdexcept>
  82.  
  83.  
  84. class ReserveFunc{
  85. public:
  86.     ReserveFunc(size_t capacity): capacity_(capacity){}
  87.     size_t GetCapacity() {return capacity_;
  88.     }
  89.  
  90. private:
  91.     size_t capacity_;
  92. };
  93.  
  94. template <typename Type>
  95. class SimpleVector {
  96. public:
  97.     using Iterator = Type*;
  98.     using ConstIterator = const Type*;
  99.  
  100.     SimpleVector() noexcept = default;
  101.  
  102.     // Создаёт вектор из size элементов, инициализированных значением по умолчанию
  103.     explicit SimpleVector(size_t size)
  104.         : SimpleVector(size, Type())
  105.     {
  106.     }
  107.  
  108.     // Создаёт вектор из size элементов, инициализированных значением value
  109.     SimpleVector(size_t size, const Type& value)
  110.         : items_(size), size_(size), capacity_(size)
  111.     {
  112.         std::fill(items_.Get(), items_.Get() + size, value);
  113.     }
  114.  
  115.     // Создаёт вектор из std::initializer_list
  116.     SimpleVector(std::initializer_list<Type> init)
  117.         : items_(init.size()), size_(init.size()), capacity_(init.size())
  118.     {
  119.         std::copy(init.begin(), init.end(), items_.Get());
  120.     }
  121.  
  122.     // конструктор копирования
  123.     SimpleVector(const SimpleVector& other)
  124.         : items_(other.capacity_), size_(other.size_)
  125.     {
  126.         std::copy(other.begin(), other.end(), items_.Get());
  127.     }
  128.  
  129.     // конструктор перемещения
  130.     SimpleVector(SimpleVector&& other)
  131.         : items_(other.capacity_)
  132.     {
  133.         swap(other);
  134.     }
  135.  
  136.     // Конструктор с вызовом функции Reserve
  137.     explicit SimpleVector(ReserveFunc obj)
  138.     {
  139.         Reserve(obj.GetCapacity());
  140.     }
  141.  
  142.     SimpleVector& operator=(const SimpleVector& rhs) {
  143.         if (&items_ != &rhs.items_) {
  144.             ArrayPtr<Type> temp(rhs.GetCapacity());
  145.             std::copy(rhs.begin(), rhs.end(), temp.Get());
  146.             items_.swap(temp);
  147.             size_ = rhs.GetSize();
  148.             capacity_ = rhs.GetCapacity();
  149.         }
  150.         return *this;
  151.     }
  152.  
  153.     // Возвращает ссылку на элемент с индексом index
  154.     Type& operator[](size_t index) noexcept {
  155.         return items_[index];
  156.     }
  157.  
  158.     // Возвращает константную ссылку на элемент с индексом index
  159.     const Type& operator[](size_t index) const noexcept {
  160.         return items_[index];
  161.     }
  162.  
  163.     // Возвращает константную ссылку на элемент с индексом index
  164.     // Выбрасывает исключение std::out_of_range, если index >= size
  165.     Type& At(size_t index) {
  166.         if (index >= size_) throw std::out_of_range("Out of range");
  167.         return items_[index];
  168.     }
  169.  
  170.     // Возвращает константную ссылку на элемент с индексом index
  171.     // Выбрасывает исключение std::out_of_range, если index >= size
  172.     const Type& At(size_t index) const {
  173.         if (index >= size_) throw std::out_of_range("Out of range");
  174.         return items_[index];
  175.     }
  176.  
  177.     // Возвращает итератор на начало массива
  178.     // Для пустого массива может быть равен (или не равен) nullptr
  179.     Iterator begin() noexcept {
  180.         return items_.Get();
  181.     }
  182.  
  183.     // Возвращает итератор на элемент, следующий за последним
  184.     // Для пустого массива может быть равен (или не равен) nullptr
  185.     Iterator end() noexcept {
  186.         return items_.Get() + size_;
  187.     }
  188.  
  189.     // Возвращает константный итератор на начало массива
  190.     // Для пустого массива может быть равен (или не равен) nullptr
  191.     ConstIterator begin() const noexcept {
  192.         return items_.Get();
  193.     }
  194.  
  195.     // Возвращает итератор на элемент, следующий за последним
  196.     // Для пустого массива может быть равен (или не равен) nullptr
  197.     ConstIterator end() const noexcept {
  198.         return items_.Get() + size_;
  199.     }
  200.  
  201.     // Возвращает константный итератор на начало массива
  202.     // Для пустого массива может быть равен (или не равен) nullptr
  203.     ConstIterator cbegin() const noexcept {
  204.         return begin();
  205.     }
  206.  
  207.     // Возвращает итератор на элемент, следующий за последним
  208.     // Для пустого массива может быть равен (или не равен) nullptr
  209.     ConstIterator cend() const noexcept {
  210.         return end();
  211.     }
  212.  
  213.     // Возвращает количество элементов в массиве
  214.     size_t GetSize() const noexcept {
  215.         return size_;
  216.     }
  217.  
  218.     // Возвращает вместимость массива
  219.     size_t GetCapacity() const noexcept {
  220.         return capacity_;
  221.     }
  222.  
  223.     // Сообщает, пустой ли массив
  224.     bool IsEmpty() const noexcept {
  225.         return size_ == 0;
  226.     }
  227.  
  228.     // Обнуляет размер массива, не изменяя его вместимость
  229.     void Clear() noexcept {
  230.         size_ = 0;
  231.     }
  232.  
  233.     // на замену std::fill для поддержки семантики перемещения метода Resize
  234.     void Fill(Iterator first, Iterator last) {
  235.         assert(first < last);
  236.  
  237.         for (; first != last; ++first) {
  238.             *first = std::move(Type());
  239.         }
  240.     }
  241.  
  242.     // Изменяет размер массива.
  243.     // При увеличении размера новые элементы получают значение по умолчанию для типа Type
  244.     void Resize(size_t new_size) {
  245.         if (new_size <= size_) {
  246.             size_ = new_size;
  247.         }
  248.         if (new_size <= capacity_) {
  249.             Fill(items_.Get() + size_, items_.Get() + size_ + new_size);
  250.         }
  251.         if (new_size > capacity_) {
  252.             size_t new_capacity = std::max(new_size, capacity_ * 2);
  253.             ArrayPtr<Type> temp(new_capacity);
  254.             Fill(temp.Get(), temp.Get() + new_capacity);
  255.             std::move(items_.Get(), items_.Get() + capacity_, temp.Get());
  256.             items_.swap(temp);
  257.  
  258.             size_ = new_size;
  259.             capacity_ = new_capacity;
  260.         }
  261.     }
  262.  
  263.     // Добавляет элемент в конец вектора
  264.     // При нехватке места увеличивает вдвое вместимость вектора
  265.     void PushBack(const Type& item) {
  266.         if (size_ + 1 > capacity_) {
  267.             size_t new_capacity = std::max(size_ + 1, capacity_ * 2);
  268.             ArrayPtr<Type> temp(new_capacity);
  269.             std::fill(temp.Get(), temp.Get() + new_capacity, Type());
  270.             std::copy(items_.Get(), items_.Get() + size_, temp.Get());
  271.             items_.swap(temp);
  272.             capacity_ = new_capacity;
  273.         }
  274.         items_[size_] = item;
  275.         ++size_;
  276.     }
  277.  
  278.     // метод PushBack перемещением
  279.     void PushBack(Type&& item) {
  280.         if (size_ + 1 > capacity_) {
  281.             size_t new_capacity = std::max(size_ + 1, capacity_ * 2);
  282.             ArrayPtr<Type> temp(new_capacity);
  283.             std::move(items_.Get(), items_.Get() + size_, temp.Get());
  284.             items_.swap(temp);
  285.             capacity_ = new_capacity;
  286.         }
  287.         items_[size_] = std::move(item);
  288.         ++size_;
  289.     }
  290.  
  291.     // Вставляет значение value в позицию pos.
  292.     // Возвращает итератор на вставленное значение
  293.     // Если перед вставкой значения вектор был заполнен полностью,
  294.     // вместимость вектора должна увеличиться вдвое, а для вектора вместимостью 0 стать равной 1
  295.     Iterator Insert(ConstIterator pos, const Type& value) {
  296.         assert(pos >= begin() && pos <= end());
  297.  
  298.         size_t count = pos - items_.Get();
  299.         if (capacity_ == 0) {
  300.             ArrayPtr<Type> temp(1);
  301.             temp[count] = value;
  302.             items_.swap(temp);
  303.             ++capacity_;
  304.         }
  305.         else if (size_ < capacity_) {
  306.             std::copy_backward(items_.Get() + count, items_.Get() + size_,
  307.                 items_.Get() + size_ + 1);
  308.             items_[count] = value;
  309.         }
  310.         else {
  311.             size_t new_capacity = std::max(size_ + 1, capacity_ * 2);
  312.             ArrayPtr<Type> temp(capacity_);
  313.             std::copy(items_.Get(), items_.Get() + size_,
  314.                 temp.Get());
  315.             std::copy_backward(items_.Get() + count, items_.Get() + size_,
  316.                 temp.Get() + size_ + 1);
  317.             temp[count] = value;
  318.             items_.swap(temp);
  319.             capacity_ = new_capacity;
  320.         }
  321.         ++size_;
  322.  
  323.         return &items_[count];
  324.     }
  325.  
  326.     // метод Insert перемещением
  327.     Iterator Insert(Iterator pos, Type&& value) {
  328.         assert(pos >= begin() && pos <= end());
  329.  
  330.         size_t count = pos - items_.Get();
  331.         if (capacity_ == 0) {
  332.             ArrayPtr<Type> temp(1);
  333.             temp[count] = std::move(value);
  334.             items_.swap(temp);
  335.             ++capacity_;
  336.         }
  337.         else if (size_ < capacity_) {
  338.             std::move_backward(items_.Get() + count, items_.Get() + size_,
  339.                 items_.Get() + size_ + 1);
  340.             items_[count] = std::move(value);
  341.         }
  342.         else {
  343.             size_t new_capacity = std::max(size_ + 1, capacity_ * 2);
  344.             ArrayPtr<Type> temp(capacity_);
  345.             std::move(items_.Get(), items_.Get() + size_,
  346.                 temp.Get());
  347.             std::move_backward(items_.Get() + count, items_.Get() + size_,
  348.                 temp.Get() + size_ + 1);
  349.             temp[count] = std::move(value);
  350.             items_.swap(temp);
  351.             capacity_ = new_capacity;
  352.         }
  353.         ++size_;
  354.  
  355.         return &items_[count];
  356.     }
  357.  
  358.     // "Удаляет" последний элемент вектора. Вектор не должен быть пустым
  359.     void PopBack() noexcept {
  360.         if (items_) --size_;
  361.     }
  362.  
  363.     // Удаляет элемент вектора в указанной позиции
  364.     Iterator Erase(ConstIterator pos) {
  365.         assert(pos != this->end());
  366.  
  367.         size_t count = pos - items_.Get();
  368.         std::move(items_.Get() + count + 1, items_.Get() + size_, items_.Get() + count);
  369.         --size_;
  370.  
  371.         return &items_[count];
  372.     }
  373.  
  374.     // Обменивает значение с другим вектором
  375.     void swap(SimpleVector& other) noexcept {
  376.         std::swap(capacity_, other.capacity_);
  377.         std::swap(size_, other.size_);
  378.         items_.swap(other.items_);
  379.     }
  380.  
  381.     void Reserve(size_t new_capacity) {
  382.         if (new_capacity > capacity_) {
  383.             ArrayPtr<Type> temp(new_capacity);
  384.             std::fill(temp.Get(), temp.Get() + new_capacity, Type());
  385.             std::copy(items_.Get(), items_.Get() + size_, temp.Get());
  386.             items_.swap(temp);
  387.             capacity_ = new_capacity;
  388.         }
  389.     }
  390.  
  391. private:
  392.     ArrayPtr<Type> items_;
  393.     size_t size_ = 0;
  394.     size_t capacity_ = 0;
  395. };
  396.  
  397. ReserveFunc Reserve(size_t capacity_to_reserve) {
  398.     return ReserveFunc(capacity_to_reserve);
  399. }
  400.  
  401. template <typename Type>
  402. inline bool operator==(const SimpleVector<Type>& lhs, const SimpleVector<Type>& rhs) {
  403.     return std::equal(lhs.begin(), lhs.end(),
  404.                       rhs.begin());
  405. }
  406.  
  407. template <typename Type>
  408. inline bool operator!=(const SimpleVector<Type>& lhs, const SimpleVector<Type>& rhs) {
  409.     return !std::equal(lhs.begin(), lhs.end(),
  410.                        rhs.begin());
  411. }
  412.  
  413. template <typename Type>
  414. inline bool operator<(const SimpleVector<Type>& lhs, const SimpleVector<Type>& rhs) {
  415.     return std::lexicographical_compare(lhs.begin(), lhs.end(),
  416.                                         rhs.begin(), rhs.end());
  417. }
  418.  
  419. template <typename Type>
  420. inline bool operator<=(const SimpleVector<Type>& lhs, const SimpleVector<Type>& rhs) {
  421.     return (lhs < rhs || lhs == rhs);
  422. }
  423.  
  424. template <typename Type>
  425. inline bool operator>(const SimpleVector<Type>& lhs, const SimpleVector<Type>& rhs) {
  426.     return rhs < lhs;
  427. }
  428.  
  429. template <typename Type>
  430. inline bool operator>=(const SimpleVector<Type>& lhs, const SimpleVector<Type>& rhs) {
  431.     return !(lhs < rhs);
  432. }
  433. /*------------------------------------------------main-----------------------------------------------------------*/
  434. #include "simple_vector.h"
  435.  
  436. #include <cassert>
  437. #include <iostream>
  438. #include <numeric>
  439.  
  440. using namespace std;
  441.  
  442. class X {
  443. public:
  444.     X()
  445.         : X(5) {
  446.     }
  447.     X(size_t num)
  448.         : x_(num) {
  449.     }
  450.     X(const X& other) = delete;
  451.     X& operator=(const X& other) = delete;
  452.     X(X&& other) {
  453.         x_ = exchange(other.x_, 0);
  454.     }
  455.     X& operator=(X&& other) {
  456.         x_ = exchange(other.x_, 0);
  457.         return *this;
  458.     }
  459.     size_t GetX() const {
  460.         return x_;
  461.     }
  462.  
  463. private:
  464.     size_t x_;
  465. };
  466.  
  467. SimpleVector<int> GenerateVector(size_t size) {
  468.     SimpleVector<int> v(size);
  469.     iota(v.begin(), v.end(), 1);
  470.     return v;
  471. }
  472.  
  473. void TestTemporaryObjConstructor() {
  474.     const size_t size = 1000000;
  475.     cout << "Test with temporary object, copy elision" << endl;
  476.     SimpleVector<int> moved_vector(GenerateVector(size));
  477.     assert(moved_vector.GetSize() == size);
  478.     cout << "Done!" << endl << endl;
  479. }
  480.  
  481. void TestTemporaryObjOperator() {
  482.     const size_t size = 1000000;
  483.     cout << "Test with temporary object, operator=" << endl;
  484.     SimpleVector<int> moved_vector;
  485.     assert(moved_vector.GetSize() == 0);
  486.     moved_vector = GenerateVector(size);
  487.     assert(moved_vector.GetSize() == size);
  488.     cout << "Done!" << endl << endl;
  489. }
  490.  
  491. void TestNamedMoveConstructor() {
  492.     const size_t size = 1000000;
  493.     cout << "Test with named object, move constructor" << endl;
  494.     SimpleVector<int> vector_to_move(GenerateVector(size));
  495.     assert(vector_to_move.GetSize() == size);
  496.  
  497.     SimpleVector<int> moved_vector(move(vector_to_move));
  498.     assert(moved_vector.GetSize() == size);
  499.     assert(vector_to_move.GetSize() == 0);
  500.     cout << "Done!" << endl << endl;
  501. }
  502.  
  503. void TestNamedMoveOperator() {
  504.     const size_t size = 1000000;
  505.     cout << "Test with named object, operator=" << endl;
  506.     SimpleVector<int> vector_to_move(GenerateVector(size));
  507.     assert(vector_to_move.GetSize() == size);
  508.  
  509.     SimpleVector<int> moved_vector = move(vector_to_move);
  510.     assert(moved_vector.GetSize() == size);
  511.     assert(vector_to_move.GetSize() == 0);
  512.     cout << "Done!" << endl << endl;
  513. }
  514.  
  515. void TestNoncopiableMoveConstructor() {
  516.     const size_t size = 5;
  517.     cout << "Test noncopiable object, move constructor" << endl;
  518.     SimpleVector<X> vector_to_move;
  519.     for (size_t i = 0; i < size; ++i) {
  520.         vector_to_move.PushBack(X(i));
  521.     }
  522.  
  523.     SimpleVector<X> moved_vector = move(vector_to_move);
  524.     assert(moved_vector.GetSize() == size);
  525.     assert(vector_to_move.GetSize() == 0);
  526.  
  527.     for (size_t i = 0; i < size; ++i) {
  528.         assert(moved_vector[i].GetX() == i);
  529.     }
  530.     cout << "Done!" << endl << endl;
  531. }
  532.  
  533. void TestNoncopiablePushBack() {
  534.     const size_t size = 5;
  535.     cout << "Test noncopiable push back" << endl;
  536.     SimpleVector<X> v;
  537.     for (size_t i = 0; i < size; ++i) {
  538.         v.PushBack(X(i));
  539.     }
  540.  
  541.     assert(v.GetSize() == size);
  542.  
  543.     for (size_t i = 0; i < size; ++i) {
  544.         assert(v[i].GetX() == i);
  545.     }
  546.     cout << "Done!" << endl << endl;
  547. }
  548.  
  549. void TestNoncopiableInsert() {
  550.     const size_t size = 5;
  551.     cout << "Test noncopiable insert" << endl;
  552.     SimpleVector<X> v;
  553.     for (size_t i = 0; i < size; ++i) {
  554.         v.PushBack(X(i));
  555.     }
  556.  
  557.     // в начало
  558.     v.Insert(v.begin(), X(size + 1));
  559.     assert(v.GetSize() == size + 1);
  560.     assert(v.begin()->GetX() == size + 1);
  561.     // в конец
  562.     v.Insert(v.end(), X(size + 2));
  563.     assert(v.GetSize() == size + 2);
  564.     assert((v.end() - 1)->GetX() == size + 2);
  565.     // в середину
  566.     v.Insert(v.begin() + 3, X(size + 3));
  567.     assert(v.GetSize() == size + 3);
  568.     assert((v.begin() + 3)->GetX() == size + 3);
  569.     cout << "Done!" << endl << endl;
  570. }
  571.  
  572. void TestNoncopiableErase() {
  573.     const size_t size = 3;
  574.     cout << "Test noncopiable erase" << endl;
  575.     SimpleVector<X> v;
  576.     for (size_t i = 0; i < size; ++i) {
  577.         v.PushBack(X(i));
  578.     }
  579.  
  580.     auto it = v.Erase(v.begin());
  581.     assert(it->GetX() == 1);
  582.     cout << "Done!" << endl << endl;
  583. }
  584.  
  585. int main() {
  586.     TestTemporaryObjConstructor();
  587.     TestTemporaryObjOperator();
  588.     TestNamedMoveConstructor();
  589.     TestNamedMoveOperator();
  590.     TestNoncopiableMoveConstructor();
  591.     TestNoncopiablePushBack();
  592.     TestNoncopiableInsert();
  593.     TestNoncopiableErase();
  594.     return 0;
  595. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement