Подтвердить что ты не робот

Shared_ptr с ресурсами без указателя

В С++ 11 можно использовать shared_ptr для управления ресурсами, отличными от указателей?


Можно использовать unique_ptr для управления ресурсами, отличными от указателей. Это делается путем реализации пользовательского класса делетера, который обеспечивает:

  • A typedef {TYPE} pointer; где {TYPE} - тип ресурса не указателя
  • operator()(pointer), который освобождает контролируемый ресурс

..., а затем создавая экземпляр a unique_ptr с пользовательским делетером в качестве второго параметра шаблона.

Например, под Windows можно создать unique_ptr, который управляет дескриптором управления . Этот тип дескриптора не освобождается, вызывая delete, но вызывая CloseServiceHandle(). Вот пример кода, который делает это:

Custom Deleter

struct SvcHandleDeleter
{
    typedef SC_HANDLE pointer;
    SvcHandleDeleter() {};

    template<class Other> SvcHandleDeleter(const Other&) {};

    void operator()(pointer h) const
    {
        CloseServiceHandle(h);
    }
};


typedef std::unique_ptr<SC_HANDLE,SvcHandleDeleter> unique_sch;

Инстанцирование

unique_sch scm(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS));

Можно ли использовать shared_ptr для управления ресурсом не указателя?

В соответствии с документацией существуют shared_ptr перегрузки конструктора, которые принимают, предоставляют средства для предоставления пользовательского класса делетера, но ни один из конструкторы принимают тип ресурса, который не является указателем или оболочкой вокруг указателя.

Как это можно сделать?

4b9b3361

Ответ 1

К сожалению, необходимость shared_ptr для стирания стилей делает невозможным выполнение текущего интерфейса для достижения именно того, что вы хотите. unique_ptr удается это сделать, потому что у него есть статическая информация о фактическом типе делетера, откуда он может нарисовать фактический тип "указатель". В случае shared_ptr тип удаления теряется в процессе стирания типа (поэтому вы не можете указать его в шаблоне shared_ptr).

Также обратите внимание, что unique_ptr не предоставляет никаких конструкторов преобразования, таких как shared_ptr (например, template<class Y> shared_ptr(Y* p)). Он не может этого сделать, потому что pointer не обязательно является реальным типом указателя и поэтому не может ограничивать то, что может быть принято (за исключением, может быть, через некоторую SFINAE с std::is_convertible_to или что-то в этом роде... но я отвлекаюсь).

Теперь одним очевидным обходным решением является просто new дескриптор ресурса, как тупой, как он звучит.:/

std::shared_ptr<SC_HANDLE> sp(new SC_HANDLE(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)),
    [](SC_HANDLE* p){ ::CloseServiceHandle(*p); delete p; });

Ответ 2

Ну, shared_ptr вызовет деструктор объекта с указателем на объект, как только будет выслана последняя ссылка на указатель, то все, что содержит класс, может быть выпущено. Просто введите класс, возможно, вот так:

struct SvcHandle
{
  typedef SC_HANDLE pointer;
  SvcHandle()
  :M_handle(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS))
  { }

  ~SvcHandle()
  {
      CloseServiceHandle(M_handle);
  }
private:
  pointer M_handle;
};

Затем просто сделайте общий указатель с новым SvcHandle. Управление жизненным циклом дескриптора будет осуществляться с помощью shared_ptr - RAII в лучшем виде.

Ответ 3

Как насчет этого?

auto scm = make_shared<unique_sch>(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS));

unique_sch - это класс, который вы упомянули в своем вопросе. Теперь используйте scm как общий указатель на ваш ресурс. Это не самая приятная вещь для разыменования, когда это необходимо, но вы спросили, возможно ли это.

Но это все еще используется указатель. Как видно из документации, класс unique_ptr принимает класс указатель в качестве параметра конструктора, который на самом деле может быть чем угодно. shared_ptr, однако берет тип в качестве параметра шаблона и принудительно превращает его в указатель на этот тип в своем конструкторе:

шаблон < класс Y, класс Deleter > shared_ptr ( Y * ptr, Deleter d);

Я думаю, что можно с уверенностью сказать, что он не может напрямую использоваться для управления ресурсом не указателя, потому что класс shared_ptr делает предположение, что его ресурс является указателем.

Ответ 4

Не думай. Поскольку стандарт предоставляет такие конструкторы для shared_ptr и ни один другой.

// 20.7.2.2.1, constructors:
constexpr shared_ptr() noexcept;
template<class Y> explicit shared_ptr(Y* p);
template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d)
template <class D, class A> shared_ptr(nullptr_t p, D d, A a)
template<class Y> shared_ptr(const shared_ptr<Y>& r, T *p) noexcept;
shared_ptr(const shared_ptr& r) noexcept;
template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
shared_ptr(shared_ptr&& r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y>&& r) noexcept;
template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);
template<class Y> shared_ptr(auto_ptr<Y>&& r);
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
constexpr shared_ptr(nullptr_t) : shared_ptr() { }

И как вы хотите сделать, например (для unique_ptr)

pointer release() noexcept;

1 Postcondition: get() == nullptr. 2 Возвраты: значение get() было у начало вызова для выпуска.

Без ресурса указателя? Вы пытаетесь взломать язык. Это всегда плохая идея.