#cpp #cpp11
Хочу реализовать несколько классов менеджеров и логгер с возможностью создания в единственном экземпляре посредством наследования от синглтона. Сейчас остановился на такой реализации: templateclass Singleton { public: template static std::shared_ptr get_instance(Args... args) { if (!instance_) { instance_ = new T(std::forward (args)...); } return instance_; } static void destroy_instance() { instance_.reset(); } private: static std::shared_ptr instance_; }; template std::shared_ptr Singleton ::instance_ = nullptr; Вопросы следующие: Правильно ли я понимаю, что паттерн «синглтон с контролем времени жизни объекта» подразумевает наличие метода типа destroy? Начинал я с классического синглтона Мейерса со статической локальной переменной в функции instance, но удалить таковую было нельзя (деструктурирование после выхода из main было неудобно по ряду причин - одна из них - было несколько подобных объектов, зависящих друг от друга, порядок удаления неопределен). Поэтому решил реализовать что-то с удалением. Является ли происходящее в GetInstance потокобезопасным? Не очень знаком с моделью памяти еще, надо ли использовать std::call_once? Какие проблемы есть в такой реализации и как ее можно улучшить? Помимо использования, например, std::unique_ptr. Может, есть вообще другая реализация, принятая в современном мире?
Ответы
Ответ 1
Первое, если нужно контролировать время жизни, то естественно нужен метод, который можно вызвать для создания и метод для удаления объекта. Назвать оные можно как угодно, но они должны быть доступны. Второе, потокобезопасным, происходящее в GetInstance, естественно, не является. Хотите потокобезопасную инициализацию используйте мьютексы с двойной блокировкой (в C++11 в этом нет смысла), либо же просто используйте std::call_once (это нормальное C++11-решение). Но тут встаёт вопрос, если у нас есть потокобезопасное создание, то как быть с удалением? Его тоже нужно делать потокобезопасным? А как это сделать? Что делать если один поток удалил, а второй пытается использовать? На эти вопросы Вам нужно ответить самостоятельно. Третье, помимо упомянутого выше, есть пара замечаний. Возвращать shared_ptr не имеет смысла: возвращайте «сырой» указатель (а ещё лучше — ссылку). Зачем Вам нужна механика shared_ptr в интерфейсе? Дальше, если уж создаёте shared_ptr, то используйте make_shared: это стандарт де-факто, да и эффективнее оно. Ну и применив предыдущий совет, получается, что shared_ptr вообще не нужен — достаточно unique_ptr. P.S. используя Вашу реализацию, наследование вообще не нужно. Достаточно использовать так: using Logger_t = Singleton; //... Logger_t::get_instance()->logMe("me the logger!"); Ответ 2
У таких глобальных сущностей есть ряд глобальных проблем - они нарушают ряд идей экономного проектирования, как то: явное лучше неявного поменьше сцепления По первому пункту - явное указание зависимости между частями программы позволяет легко превратить ее в подводную лодку - хорошо структурированное изделие, легко разделяемое на отдельные, самостоятельные отсеки. В моей практике есть случай, причем прямо связанный с логированием - предполагалось, что в продукте все параллельные дела будут делаться только в мультипроцессе, и никогда - в мультитреде, поэтому класс логирования был всегда однопоточным. И тут, 20 лет спустя, вдруг оказалось, что одна операция ну вот прекрасно делается в мультитреде - а все завязано на то самое логирование. В итоге закрытие нехитрой фичи заняло уйму времени на форк отдельного, более умного логирования, способного на мультитред. Теперь в одной части проекта одно логирование, а в другой - другое. Вывод любая, подложенная под основу проекта глобальная сущность, да еще с автоматическим времени жизни, на каком-то этапе развития проекта вылезет вам боком, причем очень сильно. на этапе раннего проектирования невозможно предусмотреть все повороты судьбы проекта и окружающих его технологий в будущем. Итого, чтобы у вас все было хорошо и красиво - реализуйте зависимости явно. Одно только наличие в C++ таких глобальных чудес как, cin, cout, new, delete создают столько чудес при мультимодульной мультикомпиляторной сборке, что лучше бы этих глобальностей не было ВОВСЕ.
Комментариев нет:
Отправить комментарий