Advertisement
chevengur

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

Apr 23rd, 2024
900
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.32 KB | None | 0 0
  1. octopus.h
  2.  
  3. #pragma once
  4.  
  5. // Тут можно подключить scopedptr.h и ptrvector.h,
  6. // если они вам понадобятся.
  7. #include "ptrvector.h"
  8. #include "scopedptr.h"
  9. #include <new> // Для исключения bad_alloc
  10. #include <vector>
  11.  
  12. // Щупальце
  13. class Tentacle {
  14. public:
  15.     explicit Tentacle(int id) noexcept
  16.         : id_(id) {
  17.     }
  18.  
  19.     int GetId() const noexcept {
  20.         return id_;
  21.     }
  22.  
  23.     Tentacle* GetLinkedTentacle() const noexcept {
  24.         return linked_tentacle_;
  25.     }
  26.     void LinkTo(Tentacle& tentacle) noexcept {
  27.         linked_tentacle_ = &tentacle;
  28.     }
  29.     void Unlink() noexcept {
  30.         linked_tentacle_ = nullptr;
  31.     }
  32.  
  33. private:
  34.     int id_ = 0;
  35.     Tentacle* linked_tentacle_ = nullptr;
  36. };
  37.  
  38. // Осьминог
  39. class Octopus {
  40. public:
  41.     Octopus()
  42.         : Octopus(8) {
  43.     }
  44.  
  45.     explicit Octopus(int num_tentacles) {
  46.         Tentacle* t = nullptr;
  47.         try {
  48.             for (int i = 1; i <= num_tentacles; ++i) {
  49.                 t = new Tentacle(i);      // Может выбросить исключение bad_alloc
  50.                 tentacles_.GetItems().push_back(t);  // Может выбросить исключение bad_alloc
  51.                
  52.                 // Обнуляем указатель на щупальце, которое уже добавили в tentacles_,
  53.                 // чтобы не удалить его в обработчике catch повторно
  54.                 t = nullptr;
  55.             }
  56.         }
  57.         catch (const std::bad_alloc&) {
  58.             // Удаляем щупальца, которые успели попасть в контейнер tentacles_
  59.             Cleanup();
  60.             // Удаляем щупальце, которое создали, но не добавили в tentacles_
  61.             delete t;
  62.             // Конструктор не смог создать осьминога с восемью щупальцами,
  63.             // поэтому выбрасываем исключение, чтобы сообщить вызывающему коду об ошибке
  64.             // throw без параметров внутри catch выполняет ПЕРЕВЫБРОС пойманного исключения
  65.             throw;
  66.         }
  67.     }
  68.  
  69.     ~Octopus() {
  70.         // Осьминог владеет объектами в динамической памяти (щупальца),
  71.         // которые должны быть удалены при его разрушении.
  72.         // Деструктор - лучшее место, чтобы прибраться за собой.
  73.         Cleanup();
  74.     }
  75.  
  76.     // Добавляет новое щупальце с идентификатором,
  77.     // равным (количество_щупалец + 1):
  78.     // 1, 2, 3, ...
  79.     // Возвращает ссылку на добавленное щупальце
  80.     Tentacle& AddTentacle() {
  81.         ScopedPtr<Tentacle>tentacle(new Tentacle(GetTentacleCount() + 1));
  82.         tentacles_.GetItems().push_back(tentacle.GetRawPtr());
  83.         tentacle.Release();
  84.         return *tentacles_.GetItems().back();
  85.     }
  86.  
  87.     int GetTentacleCount() const noexcept {
  88.         return static_cast<int>(tentacles_.GetItems().size());
  89.     }
  90.  
  91.     const Tentacle& GetTentacle(size_t index) const {
  92.         return *tentacles_.GetItems().at(index);
  93.     }
  94.     Tentacle& GetTentacle(size_t index) {
  95.         return *tentacles_.GetItems().at(index);
  96.     }
  97.  
  98. private:
  99.     void Cleanup() {
  100.         // Удаляем щупальца осьминога из динамической памяти
  101.         for (Tentacle* t : tentacles_.GetItems()) {
  102.             delete t;
  103.         }
  104.         // Очищаем массив указателей на щупальца
  105.         tentacles_.GetItems().clear();
  106.     }
  107.    
  108.     // Вектор хранит указатели на щупальца. Сами объекты щупалец находятся в куче
  109.    
  110.     PtrVector<Tentacle> tentacles_;
  111. };
  112.  
  113. =======================================================================================================================================
  114.  
  115. ptrvector.h
  116.  
  117. #pragma once
  118.  
  119. #include <vector>
  120.  
  121. template <typename T>
  122. class PtrVector {
  123. public:
  124.     PtrVector() = default;
  125.  
  126.     // Создаёт вектор указателей на копии объектов из other
  127.     PtrVector(const PtrVector& other) {
  128.         // Резервируем место в vector-е для хранения нужного количества элементов
  129.         // Благодаря этому при push_back не будет выбрасываться исключение
  130.         items_.reserve(other.items_.size());
  131.  
  132.         try {
  133.             for (auto p : other.items_) {
  134.                 // Копируем объект, если указатель на него ненулевой
  135.                 auto p_copy = p ? new T(*p) : nullptr;  // new может выбросить исключение
  136.  
  137.                 // Не выбросит исключение, т. к. в vector память уже зарезервирована
  138.                 items_.push_back(p_copy);
  139.             }
  140.         }
  141.         catch (...) {
  142.             // удаляем элементы в векторе и перевыбрасываем пойманное исключение
  143.             DeleteItems();
  144.             throw;
  145.         }
  146.     }
  147.  
  148.     // Деструктор удаляет объекты в куче, на которые ссылаются указатели,
  149.     // в векторе items_
  150.     ~PtrVector() {
  151.         DeleteItems();
  152.     }
  153.  
  154.     // Возвращает ссылку на вектор указателей
  155.     std::vector<T*>& GetItems() noexcept {
  156.         return items_;
  157.     }
  158.  
  159.     // Возвращает константную ссылку на вектор указателей
  160.     std::vector<T*> const& GetItems() const noexcept {
  161.         return items_;
  162.     }
  163.  
  164. private:
  165.     void DeleteItems() noexcept {
  166.         for (auto p : items_) {
  167.             delete p;
  168.         }
  169.     }
  170.  
  171.     std::vector<T*> items_;
  172. };
  173.  
  174. =======================================================================================================================================
  175.  
  176. scopedptr.h
  177.  
  178. #pragma once
  179.  
  180. #include <stdexcept> // содержит std::logic_error
  181.  
  182. template <typename T>
  183. class ScopedPtr {
  184. public:
  185.     ScopedPtr() = default;
  186.  
  187.     explicit ScopedPtr(T* raw_ptr) noexcept
  188.         : ptr_(raw_ptr) {
  189.     }
  190.  
  191.     // Запрещаем копирование указателя
  192.     ScopedPtr(const ScopedPtr&) = delete;
  193.  
  194.     ~ScopedPtr() {
  195.         delete ptr_;
  196.     }
  197.  
  198.     T* GetRawPtr() const noexcept {
  199.         return ptr_;
  200.     }
  201.  
  202.     T* Release() noexcept {
  203.         T* p = ptr_;
  204.         ptr_ = nullptr;
  205.         return p;
  206.     }
  207.  
  208.     explicit operator bool() const {
  209.         return ptr_ != nullptr;
  210.     }
  211.  
  212.     T* operator->() const {
  213.         using namespace std::literals;
  214.         if (!ptr_) {
  215.             throw std::logic_error("Scoped ptr is null"s);
  216.         }
  217.         return ptr_;
  218.     }
  219.  
  220.     T& operator*() const {
  221.         using namespace std::literals;
  222.         if (!ptr_) {
  223.             throw std::logic_error("Scoped ptr is null"s);
  224.         }
  225.         return *ptr_;
  226.     }
  227.  
  228. private:
  229.     T* ptr_ = nullptr;
  230. };
  231.  
  232. =======================================================================================================================================
  233.  
  234. main.cpp
  235.  
  236. #include "octopus.h"
  237.  
  238. #include <cassert>
  239. #include <iostream>
  240.  
  241. using namespace std;
  242.  
  243. int main() {
  244.     // Проверка конструирования осьминогов
  245.     {
  246.         // По умолчанию осьминог имеет 8 щупалец
  247.         Octopus default_octopus;
  248.         assert(default_octopus.GetTentacleCount() == 8);
  249.  
  250.         // Осьминог может иметь отличное от 8 количество щупалец
  251.         Octopus quadropus(4);
  252.         assert(quadropus.GetTentacleCount() == 4);
  253.  
  254.         // И даже вообще не иметь щупалец
  255.         Octopus coloboque(0);
  256.         assert(coloboque.GetTentacleCount() == 0);
  257.     }
  258.  
  259.     // Осьминогу можно добавлять щупальца
  260.     {
  261.         Octopus octopus(1);
  262.         Tentacle* t0 = &octopus.GetTentacle(0);
  263.         Tentacle* t1 = &octopus.AddTentacle();
  264.         assert(octopus.GetTentacleCount() == 2);
  265.         Tentacle* t2 = &octopus.AddTentacle();
  266.         assert(octopus.GetTentacleCount() == 3);
  267.  
  268.         // После добавления щупалец ранее созданные щупальца не меняют своих адресов
  269.         assert(&octopus.GetTentacle(0) == t0);
  270.         assert(&octopus.GetTentacle(1) == t1);
  271.         assert(&octopus.GetTentacle(2) == t2);
  272.  
  273.         for (int i = 0; i < octopus.GetTentacleCount(); ++i) {
  274.             assert(octopus.GetTentacle(i).GetId() == i + 1);
  275.         }
  276.     }
  277.  
  278.     // Осьминоги могут прицепляться к щупальцам друг друга
  279.     {
  280.         Octopus male(2);
  281.         Octopus female(2);
  282.  
  283.         assert(male.GetTentacle(0).GetLinkedTentacle() == nullptr);
  284.  
  285.         male.GetTentacle(0).LinkTo(female.GetTentacle(1));
  286.         assert(male.GetTentacle(0).GetLinkedTentacle() == &female.GetTentacle(1));
  287.  
  288.         male.GetTentacle(0).Unlink();
  289.         assert(male.GetTentacle(0).GetLinkedTentacle() == nullptr);
  290.     }
  291.  
  292.     // Копия осьминога имеет свою собственную копию щупалец, которые
  293.     // копируют состояние щупалец оригинального осьминога
  294.     {
  295.         // Перебираем осьминогов с разным количеством щупалец
  296.         for (int num_tentacles = 0; num_tentacles < 10; ++num_tentacles) {
  297.             Octopus male(num_tentacles);
  298.             Octopus female(num_tentacles);
  299.             // Пусть они хватают друг друга за щупальца
  300.             for (int i = 0; i < num_tentacles; ++i) {
  301.                 male.GetTentacle(i).LinkTo(female.GetTentacle(num_tentacles - 1 - i));
  302.             }
  303.  
  304.             Octopus male_copy(male);
  305.             // Проверяем состояние щупалец копии
  306.             assert(male_copy.GetTentacleCount() == male.GetTentacleCount());
  307.             for (int i = 0; i < male_copy.GetTentacleCount(); ++i) {
  308.                 // Каждое щупальце копии размещается по адресу, отличному от адреса оригинального щупальца
  309.                 assert(&male_copy.GetTentacle(i) != &male.GetTentacle(i));
  310.                 // Каждое щупальце копии прицепляется к тому же щупальцу, что и оригинальное
  311.                 assert(male_copy.GetTentacle(i).GetLinkedTentacle() == male.GetTentacle(i).GetLinkedTentacle());
  312.             }
  313.         }
  314.         // Если вы видите эту надпись, то разрушение осьминогов, скорее всего,
  315.         // прошло без неопределённого поведения
  316.         cout << "Everything is OK"s << endl;
  317.     }
  318. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement