Advertisement
chevengur

СПРИНТ № 7 | Модель памяти в C++ | Урок 9: Копирование объектов. Часть третья 1/2

Apr 20th, 2024
863
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.42 KB | None | 0 0
  1. #include <algorithm>
  2. #include <cassert>
  3. #include <vector>
  4.  
  5.  
  6. using namespace std;
  7.  
  8. template <typename T>
  9. class PtrVector {
  10. public:
  11.     PtrVector() = default;
  12.  
  13.     // Создаёт вектор указателей на копии объектов из other
  14.     PtrVector(const PtrVector& other) {
  15.         // Резервируем место в vector-е для хранения нужного количества элементов
  16.         // Благодаря этому при push_back не будет выбрасываться исключение
  17.         items_.reserve(other.items_.size());
  18.  
  19.         try {
  20.             for (auto p : other.items_) {
  21.                 // Копируем объект, если указатель на него ненулевой
  22.                 auto p_copy = p ? new T(*p) : nullptr;  // new может выбросить исключение
  23.  
  24.                 // Не выбросит исключение, т. к. в vector память уже зарезервирована
  25.                 items_.push_back(p_copy);
  26.             }
  27.         }
  28.         catch (...) {
  29.             // удаляем элементы в векторе и перевыбрасываем пойманное исключение
  30.             DeleteItems();
  31.             throw;
  32.         }
  33.     }
  34.  
  35.     // Деструктор удаляет объекты в куче, на которые ссылаются указатели,
  36.     // в векторе items_
  37.     ~PtrVector() {
  38.         DeleteItems();
  39.     }
  40.  
  41.     // Возвращает ссылку на вектор указателей
  42.     vector<T*>& GetItems() noexcept {
  43.         return items_;
  44.     }
  45.  
  46.     // Возвращает константную ссылку на вектор указателей
  47.     vector<T*> const& GetItems() const noexcept {
  48.         return items_;
  49.     }
  50.  
  51. private:
  52.     void DeleteItems() noexcept {
  53.         for (auto p : items_) {
  54.             delete p;
  55.         }
  56.     }
  57.  
  58.     vector<T*> items_;
  59. };
  60.  
  61. // Эта функция main тестирует шаблон класса PtrVector
  62. int main() {
  63.     // Вспомогательный "шпион", позволяющий узнать о своём удалении
  64.     struct DeletionSpy {
  65.         explicit DeletionSpy(bool& is_deleted)
  66.             : is_deleted_(is_deleted) {
  67.         }
  68.         ~DeletionSpy() {
  69.             is_deleted_ = true;
  70.         }
  71.         bool& is_deleted_;
  72.     };
  73.  
  74.     // Проверяем удаление элементов
  75.     {
  76.         bool spy1_is_deleted = false;
  77.         DeletionSpy* ptr1 = new DeletionSpy(spy1_is_deleted);
  78.         {
  79.             PtrVector<DeletionSpy> ptr_vector;
  80.             ptr_vector.GetItems().push_back(ptr1);
  81.             assert(!spy1_is_deleted);
  82.  
  83.             // Константная ссылка на ptr_vector
  84.             const auto& const_ptr_vector_ref(ptr_vector);
  85.             // И константная, и неконстантная версия GetItems
  86.             // должны вернуть ссылку на один и тот же вектор
  87.             assert(&const_ptr_vector_ref.GetItems() == &ptr_vector.GetItems());
  88.         }
  89.         // При разрушении ptr_vector должен удалить все объекты, на которые
  90.         // ссылаются находящиеся внутри него указателели
  91.         assert(spy1_is_deleted);
  92.     }
  93.  
  94.     // Вспомогательный «шпион», позволяющий узнать о своём копировании
  95.     struct CopyingSpy {
  96.         explicit CopyingSpy(int& copy_count)
  97.             : copy_count_(copy_count) {
  98.         }
  99.         CopyingSpy(const CopyingSpy& rhs)
  100.             : copy_count_(rhs.copy_count_)  //
  101.         {
  102.             ++copy_count_;
  103.         }
  104.         int& copy_count_;
  105.     };
  106.  
  107.     // Проверяем копирование элементов при копировании массива указателей
  108.     {
  109.         // 10 элементов
  110.         vector<int> copy_counters(10);
  111.  
  112.         PtrVector<CopyingSpy> ptr_vector;
  113.         // Подготавливаем оригинальный массив указателей
  114.         for (auto& counter : copy_counters) {
  115.             ptr_vector.GetItems().push_back(new CopyingSpy(counter));
  116.         }
  117.         // Последний элемент содержит нулевой указатель
  118.         ptr_vector.GetItems().push_back(nullptr);
  119.  
  120.         auto ptr_vector_copy(ptr_vector);
  121.         // Количество элементов в копии равно количеству элементов оригинального вектора
  122.          assert(ptr_vector_copy.GetItems().size() == ptr_vector.GetItems().size());
  123.  
  124.         // копия должна хранить указатели на новые объекты
  125.         assert(ptr_vector_copy.GetItems() != ptr_vector.GetItems());
  126.         // Последний элемент исходного массива и его копии - нулевой указатель
  127.         assert(ptr_vector_copy.GetItems().back() == nullptr);
  128.         // Проверяем, что элементы были скопированы (копирующие шпионы увеличивают счётчики копий).
  129.         assert(all_of(copy_counters.begin(), copy_counters.end(), [](int counter) {
  130.             return counter == 1;
  131.             }));
  132.     }
  133. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement