реализация shared_instance с использованием C++ и RAII


Я создал немного singleton-как класс, который удаляется, когда нет ссылки на него уже нет (поэтому срок ее службы будет немного предсказуем). Я стремился сделать его потокобезопасным.

#include <memory>
#include <mutex>

template< typename t_type >
struct shared_instance
{
    static std::shared_ptr< t_type > get_shared_instance()
    {
        static std::weak_ptr< t_type > weak;
        static std::mutex mutex;

        auto result = weak.lock();
        if( nullptr == result )
        {
            std::unique_lock< std::mutex > lock{ mutex };

            result = weak.lock();
            if( nullptr != result )
            {
                return result;
            }

            result = std::make_shared< t_type >();
            weak = result;
        }
        return result;
    }
};

template< typename t >
struct context 
{ 
    context() 
    : instance{ shared_instance< t >::get_shared_instance() } 
    { }

private: std::shared_ptr< t > instance;
};

Я был бы рад, если бы вы могли прокомментировать реализация себя и как я могла решить номера-по умолчанию-конструктивных объектов (я не знаю точной пересылки и шаблоны с переменным числом аргументов, но я не нашел элегантный способ, чтобы оценить их лениво).



216
3
задан 28 января 2018 в 04:01 Источник Поделиться
Комментарии
1 ответ

По Умолчанию Конструктора:

Добавлена поддержка для нестандартных конструктивных объектов в "ленивый" способ, может быть сделано путем добавления заводская функция std::function<std::shared_ptr< t_type >()>.

template< typename t_type >
struct shared_instance
{
using t_factory = std::function<std::shared_ptr< t_type >()>;

static t_factory factory; // MUST be set before calling get_shared_instance

static std::shared_ptr< t_type > get_shared_instance()
{
...

assert(factory); // or throw, or return empty shared_ptr

result = factory(); // instead of make_shared

...
}

};

// and define the static member...
template< typename t_type >
typename shared_instance< t_type >::t_factory shared_instance< t_type >::factory;

...

int main()
{
context<int> c; // not ok... haven't set factory yet!

// do this before any call to get_shared_instance
shared_instance<int>::factory = [] () { return std::make_shared<int>(5); }; // store things in the lambda by value, not reference...

context<int> c; // ok
}

Я не уверен, что это считается хоть и элегантные, как это дает вам в два этапа инициализации. Это может быть исправлено путем признания самого shared_instance как своего рода фабрику, благодаря чему ее содержимое не статическим, и передает его в контексте:

template< typename t >
struct context
{
context(instance_provider< t >& provider): // now it can't be created unless this exists...
instance{ provider.get_shared_instance() }
{ }

private:

std::shared_ptr< t > instance;
};

int main()
{
auto provider = instance_provider<int>([] () { return std::make_shared<int>(5); });

context<int> c(provider);
}

Дополнительные Соображения:

Ли библиотеки C вы используете сказать что-нибудь о себе контекст жизни? (например, в winsock количество ссылок на вызовы WSAStartup() и WSACleanup() внутренне, так долго, как звонить правильно в паре библиотеки Си выполняет очистку)

Главная особенность, что этот класс shared_instance, кажется, чтобы обеспечить уничтожает экземпляр незамедлительно после последнего контекст разрушается. Сама РАИИ может быть достигнуто путем просто хранить уникальный указатель на максимально возможный срок службы любых контекстах, что будет проще. Это быстрое разрушение на самом деле важно?

Если нет, то это просто Singleton, который мы либо живем, либо сделать работу, чтобы передать указатель или ссылку на что вам нужно, где он должен быть.

2
ответ дан 6 февраля 2018 в 08:02 Источник Поделиться