rpp compartido con recursos sin puntero


En C++11 ¿es posible usar shared_ptr para controlar recursos que no son puntero?


Es posible usar unique_ptr para administrar recursos que no son puntero. Esto se hace implementando una clase deleter personalizada que proporciona:

  1. A typedef {TYPE} pointer; donde {TYPE} es el tipo de recurso sin puntero
  2. operator()(pointer) que libera el recurso controlado

...y luego instanciar un unique_ptr con el deleter personalizado como el segundo parámetro de plantilla.

Por ejemplo, bajo Windows es posible crear un unique_ptr que administra un controlador de servicio. Este tipo de identificador no se libera llamando a delete, sino llamando CloseServiceHandle(). Aquí está el código de ejemplo que hace esto:

Deleter personalizado

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;

Instanciación

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

¿Es posible usar shared_ptr para controlar también un recurso que no es puntero?

De acuerdo con la documentación, hay shared_ptr las sobrecargas del constructor que toman proporcionan significa proporcionar una clase deleter personalizada, pero ninguno de los constructores acepta un tipo de recurso que no sea un puntero o un contenedor alrededor de un puntero.

¿Cómo se puede hacer esto?

Author: John Dibling, 2012-07-25

4 answers

Lamentablemente, la necesidad de borrado de tipos de shared_ptrhace imposible con la interfaz actual lograr exactamente lo que quieres. unique_ptr se las arregla para hacer eso porque tiene información estática sobre el tipo deleter real, desde donde puede dibujar el tipo "puntero" real. En el caso de shared_ptr, el tipo deleter se pierde en el proceso type-erasure (por lo que no puede especificarlo en la plantilla shared_ptr).

También tenga en cuenta que unique_ptr no proporciona ningún constructor de conversión como shared_ptr lo hace (por ejemplo, template<class Y> shared_ptr(Y* p)). No puede hacerlo porque pointer no es necesariamente un tipo de puntero real, por lo que no puede restringir lo que se puede aceptar (excepto tal vez a través de algunas SFINAE con std::is_convertible_to o algo así... pero estoy divagando).

Ahora, una solución obvia es simplemente new el controlador de recursos, por tonto que parezca. :/

std::shared_ptr<SC_HANDLE> sp(new SC_HANDLE(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)),
    [](SC_HANDLE* p){ ::CloseServiceHandle(*p); delete p; });
 14
Author: Xeo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-25 15:37:36

Bueno, shared_ptr invocará el destructor del objeto apuntado tan pronto como se libere la última referencia al puntero, entonces cualquier cosa que contenga la clase puede ser liberada. Simplemente haga una clase tal vez como esta:

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

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

Entonces simplemente haga un puntero compartido con un nuevo SvcHandle. La gestión de la vida útil de la manija irá con el shared_ptr - RAII en su máxima expresión.

 5
Author: emsr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-27 00:52:52

¿Qué tal esto?

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

Unique_sch es la clase que mencionaste en tu pregunta. Ahora use scm como un puntero compartido a su recurso. No es lo mejor para desreferenciar cuando sea necesario, pero usted preguntó si es posible.

Pero eso sigue siendo usar un puntero. Como se puede ver en la documentación, la clase unique_ptr toma una clase pointer como parámetro constructor, que de hecho puede ser cualquier cosa. shared_ptr sin embargo, toma un tipo como template parameter and forcefully turns that into a pointer to that type on its constructor:

Template shared_ptr (Y* ptr, Deleter d);

Creo que es seguro decir que no se puede usar directamente para administrar un recurso que no sea puntero, porque la clase shared_ptr asume que su recurso es un puntero.

 1
Author: EddieBytes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-25 15:18:07

Piensa que no. Since standard proporciona tales constructores para shared_ptr y ningún otro.

// 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() { }

Y cómo quieres hacerlo, por ejemplo (para unique_ptr)

pointer release() noexcept;

1 Postcondición: get() == nullptr. 2 Devuelve: El valor get () tenía en el comienzo de la llamada a la liberación.

Sin recurso puntero? Intentas hackear el lenguaje. Siempre es mala idea.

 0
Author: ForEveR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-25 14:47:02