Advertisement
chevengur

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

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