Advertisement
kutuzzzov

Урок 4 умный указатель weak_ptr

Apr 4th, 2023
1,444
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.14 KB | None | 0 0
  1. #include <cassert>
  2. #include <iostream>
  3. #include <memory>
  4. #include <string>
  5. #include <string_view>
  6. #include <unordered_map>
  7. #include <map>
  8.  
  9. using namespace std::string_literals;
  10.  
  11. template <typename Key, typename Value, typename ValueFactoryFn>
  12. class Cache {
  13. public:
  14.     // Создаёт кэш инициализированный фабрикой значений, созданной по умолчанию
  15.     Cache() = default;
  16.  
  17.     // Создаёт кэш, инициализированный объетом, играющим роль фабрики,
  18.     // создающей (при помощи operator() значение по его ключу)
  19.     // Фабрика должна вернуть shared_ptr<Value> либо unique_ptr<Value>.
  20.     // Пример использования:
  21.     // shared_ptr<Value> value = value_factory(key);
  22.     explicit Cache(ValueFactoryFn value_factory) : value_factory_(value_factory) {
  23.         // Реализуйте конструктор самостоятельно
  24.     }
  25.  
  26.     // Возвращает закешированное значение по ключу. Если значение отсутствует или уже удалено,
  27.     // оно должно быть создано с помощью фабрики и сохранено в кэше.
  28.     // Если на объект нет внешних ссылок, он должен быть удалён из кэша
  29.     std::shared_ptr<Value> GetValue(const Key& key) {
  30.         if (cache_.count(key) == 0) {
  31.             std::shared_ptr<Value> output = value_factory_(key);
  32.  
  33.             std::weak_ptr wp_value{output};
  34.  
  35.             assert(!wp_value.expired());  // remove this
  36.  
  37.             cache_[key] = wp_value;
  38.  
  39.             return output;
  40.         }
  41.  
  42.         std::weak_ptr<Value> wp_value = cache_.at(key);
  43.  
  44.         if (wp_value.expired()) {
  45.             std::shared_ptr<Value> output = value_factory_(key);
  46.             cache_[key] = std::weak_ptr{output};
  47.             return output;
  48.         }
  49.  
  50.         assert(!wp_value.expired());  // remove this
  51.  
  52.         return wp_value.lock();
  53.     }
  54.  
  55.    
  56. private:
  57.     std::map<Key, std::weak_ptr<Value>> cache_;
  58.     ValueFactoryFn value_factory_;
  59. };
  60.  
  61. // Пример объекта, находящегося в кэше
  62. class Object {
  63. public:
  64.     explicit Object(std::string id)
  65.         : id_(std::move(id))  //
  66.     {
  67.         using namespace std;
  68.         cout << "Object '"sv << id_ << "' has been created"sv << endl;
  69.     }
  70.  
  71.     const std::string& GetId() const {
  72.         return id_;
  73.     }
  74.  
  75.     ~Object() {
  76.         using namespace std;
  77.         cout << "Object '"sv << id_ << "' has been destroyed"sv << endl;
  78.     }
  79.  
  80. private:
  81.     std::string id_;
  82. };
  83.  
  84. using ObjectPtr = std::shared_ptr<Object>;
  85.  
  86. struct ObjectFactory {
  87.     ObjectPtr operator()(std::string id) const {
  88.         return std::make_shared<Object>(std::move(id));
  89.     }
  90. };
  91.  
  92. void Test1() {
  93.     using namespace std;
  94.     ObjectPtr alice1;
  95.     // Кэш, объектов Object, создаваемых при помощи ObjectFactory,
  96.     // доступ к которым предоставляется по ключу типа string
  97.     Cache<string, Object, ObjectFactory> cache;
  98.  
  99.     // Извлекаем объекты Alice и Bob
  100.     alice1 = cache.GetValue("Alice"s);
  101.     auto bob = cache.GetValue("Bob"s);
  102.     // Должны вернуться два разных объекта с правильными id
  103.     assert(alice1 != bob);
  104.     assert(alice1->GetId() == "Alice"s);
  105.     assert(bob->GetId() == "Bob"s);
  106.  
  107.     // Повторный запрос объекта Alice должен вернуть существующий объект
  108.     auto alice2 = cache.GetValue("Alice"s);
  109.     assert(alice1 == alice2);
  110.  
  111.     // Указатель alice_wp следит за жизнью объекта Alice
  112.     weak_ptr alice_wp{alice1};
  113.     alice1.reset();
  114.     assert(!alice_wp.expired());
  115.     cout << "---"sv << endl;
  116.     alice2.reset();
  117.     // Объект Alice будет удалён, так как на него больше не ссылаются shared_ptr
  118.     assert(alice_wp.expired());
  119.     cout << "---"sv << endl;
  120.     // Объект Bob будет удалён, после разрушения указателя bob
  121.  
  122.     alice1 = cache.GetValue("Alice"s);  // объект 'Alice' будет создан заново
  123.     cout << "---"sv << endl;
  124. }
  125.  
  126. struct Book {
  127.     Book(std::string title, std::string content)
  128.         : title(std::move(title))
  129.         , content(std::move(content)) {
  130.     }
  131.  
  132.     std::string title;
  133.     std::string content;
  134. };
  135.  
  136. // Функциональный объект, загружающий книги из unordered_map
  137. class BookLoader {
  138. public:
  139.     using BookStore = std::unordered_map<std::string, std::string>;
  140.  
  141.     // Принимает константную ссылку на хранилище книг и ссылку на переменную-счётчик загрузок
  142.     explicit BookLoader(const BookStore& store, size_t& load_count) : book_store_(store), load_count_(load_count) {
  143.         // Реализуйте конструктор самостоятельно
  144.     }
  145.  
  146.     // Загружает книгу из хранилища по её названию и возвращает указатель
  147.     // В случае успешной загрузки (книга есть в хранилище)
  148.     // нужно увеличить значения счётчика загрузок load_count, переданного в конструктор, на 1.
  149.     // Если книга в хранилище отсутствует, нужно выбросить исключение std::out_of_range,
  150.     // а счётчик не увеличивать
  151.     std::shared_ptr<Book> operator()(const std::string& title) const {
  152.         // Заглушка, реализуйте метод самостоятельно
  153.         if (book_store_.count(title) == 0) {
  154.             throw std::out_of_range("no such title in book store"s);
  155.         }
  156.  
  157.         load_count_++;
  158.         return std::make_shared<Book>(title, book_store_.at(title));
  159.     }
  160.  
  161. private:
  162.     // Добавьте необходимые данные и/или методы
  163.     const BookStore& book_store_;
  164.     size_t& load_count_;
  165. };
  166.  
  167. void Test2() {
  168.     using namespace std;
  169.     // Хранилище книг.
  170.     BookLoader::BookStore books{
  171.         {"Sherlock Holmes"s,
  172.          "To Sherlock Holmes she is always the woman. I have seldom heard him mention her under any other name."s},
  173.         {"Harry Potter"s, "Chapter 1. The boy who lived. ...."s},
  174.     };
  175.     using BookCache = Cache<string, Book, BookLoader>;
  176.  
  177.     size_t load_count = 0;
  178.     // Создаём кэш, который будет использщовать BookLoader для загрузки книг из хранилища books
  179.     BookCache book_cache{BookLoader{books, load_count}};
  180.  
  181.     // Загруженная книга должна содержать данные из хранилища
  182.     auto book1 = book_cache.GetValue("Sherlock Holmes"s);
  183.     assert(book1);
  184.     assert(book1->title == "Sherlock Holmes"s);
  185.     assert(book1->content == books.at(book1->title));
  186.     assert(load_count == 1);
  187.  
  188.     // Повторный запрос книги должен возвращать закешированное значение
  189.     auto book2 = book_cache.GetValue("Sherlock Holmes"s);
  190.     assert(book2);
  191.     assert(book1 == book2);
  192.     assert(load_count == 1);
  193.  
  194.     weak_ptr<Book> weak_book{book1};
  195.     assert(!weak_book.expired());
  196.     book1.reset();
  197.     book2.reset();
  198.     // Книга удаляется, как только на неё перестают ссылаться указатели вне кэша
  199.     assert(weak_book.expired());
  200.  
  201.     book1 = book_cache.GetValue("Sherlock Holmes"s);
  202.     assert(load_count == 2);
  203.     assert(book1);
  204.  
  205.     try {
  206.         book_cache.GetValue("Fifty Shades of Grey"s);
  207.         // BookLoader выбросит исключение при попытке загрузить несуществующую книгу
  208.         assert(false);
  209.     } catch (const std::out_of_range&) {
  210.         /* Всё нормально. Такой книги нет в книгохранилище */
  211.     } catch (...) {
  212.         cout << "Unexpected exception"sv << endl;
  213.     }
  214.     // Счётчик загрузок не должен обновиться, так как книги нет в хранилище
  215.     assert(load_count == 2);
  216.  
  217.     // Добавляем книгу в хранилище
  218.     books["Fifty Shades of Grey"s] = "I scowl with frustration at myself in the mirror..."s;
  219.  
  220.     try {
  221.         auto book = book_cache.GetValue("Fifty Shades of Grey"s);
  222.         // Теперь книга должна быть успешно найдена
  223.         assert(book->content == books.at("Fifty Shades of Grey"s));
  224.     } catch (...) {
  225.         assert(false);
  226.     }
  227.     // Счётчик загрузок должен обновиться, так как книги есть в хранилище
  228.     assert(load_count == 3);
  229. }
  230.  
  231. int main() {
  232.     Test1();
  233.     Test2();
  234. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement