Advertisement
chevengur

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

Apr 19th, 2024
1,089
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.16 KB | None | 0 0
  1. #include <cassert>
  2. #include <iostream>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7.  
  8. // Умный указатель, удаляющий связанный объект при своём разрушении.
  9. // Параметр шаблона T задаёт тип объекта, на который ссылается указатель
  10. template <typename T>
  11. class ScopedPtr {
  12. public:
  13.     // Конструктор по умолчанию создаёт нулевой указатель,
  14.     // так как поле ptr_ имеет значение по умолчанию nullptr
  15.     ScopedPtr() = default;
  16.  
  17.     // Создаёт указатель, ссылающийся на переданный raw_ptr.
  18.     // raw_ptr ссылается либо на объект, созданный в куче при помощи new,
  19.     // либо является нулевым указателем
  20.     // Спецификатор noexcept обозначает, что метод не бросает исключений
  21.     explicit ScopedPtr(T* raw_ptr) noexcept {
  22.         if (raw_ptr != nullptr)
  23.         {
  24.             ptr_ = raw_ptr;
  25.         }
  26.     }
  27.  
  28.     // Удаляем у класса конструктор копирования
  29.     ScopedPtr(const ScopedPtr&) = delete;
  30.  
  31.     // Деструктор. Удаляет объект, на который ссылается умный указатель.
  32.     ~ScopedPtr() {
  33.  
  34.         delete ptr_;
  35.     }
  36.  
  37.     // Возвращает указатель, хранящийся внутри ScopedPtr
  38.     T* GetRawPtr() const noexcept {
  39.  
  40.         return ptr_;
  41.     }
  42.  
  43.     // Прекращает владение объектом, на который ссылается умный указатель.
  44.     // Возвращает прежнее значение "сырого" указателя и устанавливает поле ptr_ в null
  45.     T* Release() noexcept {
  46.         T* new_ptr = ptr_;
  47.         ptr_ = nullptr;
  48.         return new_ptr;
  49.  
  50.     }
  51.  
  52.     // Оператор -> должен возвращать указатель на объект
  53.     // Выбрасывает исключение std::logic_error, если указатель нулевой
  54.     T* operator->() const {
  55.         if (ptr_ != nullptr)
  56.         {
  57.             return ptr_;
  58.         }
  59.         throw logic_error("Null pointer");
  60.     }
  61.  
  62.     // Оператор разыменования возвращает ссылку на объект
  63.     // Выбрасывает исключение std::logic_error, если указатель нулевой
  64.     T& operator*() const {
  65.         if (ptr_ != nullptr)
  66.         {
  67.             return *ptr_;
  68.         }
  69.         throw logic_error("Null pointer");
  70.     }
  71.  
  72.     // Оператор приведения к типу bool позволяет узнать, ссылается ли умный указатель
  73.     // на какой-либо объект
  74.     explicit operator bool() const noexcept {
  75.         return ptr_ != nullptr;
  76.     }
  77.  
  78. private:
  79.     T* ptr_ = nullptr;
  80. };
  81.  
  82. // Этот main тестирует класс ScopedPtr
  83. int main() {
  84.    
  85.     // Проверка работы оператора приведения к типу bool
  86.     {
  87.         // Для нулевого указателя приведение к типу bool возвращает false
  88.         const ScopedPtr<int> empty_ptr;
  89.         assert(!empty_ptr);
  90.  
  91.         // Для ненулевого указателя приведение к типу bool возвращает true
  92.         const ScopedPtr<int> ptr_to_existing_object(new int(0));
  93.         assert(ptr_to_existing_object);
  94.  
  95.         static_assert(noexcept(static_cast<bool>(ptr_to_existing_object)));
  96.     }
  97.  
  98.     // Проверка работы оператора разыменования *
  99.     {
  100.         string* raw_ptr = new string("hello");
  101.         ScopedPtr<string> smart_ptr(raw_ptr);
  102.         // Ссылка, возвращаемая оператором разыменования, должна ссылаться на объект,
  103.         // на который указывает умный указатель
  104.         assert(&*smart_ptr == raw_ptr);
  105.  
  106.         try {
  107.             ScopedPtr<int> empty_ptr;
  108.             // При попытке разыменовать пустой указатель должно быть выброшено
  109.             // исключение logic_error
  110.             *empty_ptr;
  111.             // Сюда попасть мы не должны
  112.             assert(false);
  113.         }
  114.         catch (const logic_error&) {
  115.             // мы там, где нужно
  116.         }
  117.         catch (...) {
  118.             // Других исключений выбрасываться не должно
  119.             assert(false);
  120.         }
  121.     }
  122.  
  123.     // Проверка работы оператора ->
  124.     {
  125.         string* raw_ptr = new string("hello");
  126.         ScopedPtr<string> smart_ptr(raw_ptr);
  127.         // Доступ к членам класса через умный указатель должен быть аналогичен
  128.         // доступу через "сырой" указатель
  129.         assert(smart_ptr->data() == raw_ptr->data());
  130.  
  131.         try {
  132.             ScopedPtr<string> empty_ptr;
  133.             // При попытке разыменовать пустой указатель должно быть выброшено
  134.             // исключение logic_error
  135.             empty_ptr->clear();
  136.             // Сюда попасть мы не должны
  137.             assert(false);
  138.         }
  139.         catch (const logic_error&) {
  140.             // мы там, где нужно
  141.         }
  142.         catch (...) {
  143.             // Других исключений выбрасываться не должно
  144.             assert(false);
  145.         }
  146.     }
  147.  
  148.     // Пример использования
  149.     {
  150.         // На этой структуре будет проверяться работа умного указателя
  151.         struct Object {
  152.             Object() {
  153.                 cout << "Object is default constructed"s << endl;
  154.             }
  155.             void DoSomething() {
  156.                 cout << "Doing something"s << endl;
  157.             }
  158.             ~Object() {
  159.                 cout << "Object is destroyed"s << endl;
  160.             }
  161.         };
  162.  
  163.         // Сконструированный по умолчанию указатель ссылается на nullptr
  164.         ScopedPtr<Object> empty_smart_ptr;
  165.         // Перегруженный оператор приведения к типу bool вернёт false для пустого указателя
  166.         assert(!empty_smart_ptr);
  167.  
  168.         ScopedPtr<Object> smart_ptr(new Object());
  169.         // Перегруженный оператор bool вернёт true для указателя, ссылающегося на объект
  170.         assert(smart_ptr);
  171.  
  172.         // Проверка оператора разыменования
  173.         (*smart_ptr).DoSomething();
  174.         // Проверка оператора доступа к членам класса
  175.         smart_ptr->DoSomething();
  176.     }
  177. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement