c++ - smart - shared_ptr con recursos no punteros
unique pointer (4)
¿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 la mejor manera de desreferenciar cuando es necesario, pero usted preguntó si es posible.
Pero eso sigue usando un puntero. Como se puede ver en la documentación, la clase unique_ptr toma una clase de puntero como un parámetro de constructor, que de hecho puede ser cualquier cosa. shared_ptr, sin embargo, toma un tipo como parámetro de plantilla y lo convierte en un puntero a ese tipo en su constructor:
plantilla <clase Y, clase Deleter> shared_ptr ( Y * ptr, Deleter d);
Creo que es seguro decir que no se puede usar directamente para administrar un recurso que no es puntero, porque la clase shared_ptr asume que su recurso es un puntero.
En C ++ 11, ¿es posible usar shared_ptr
para controlar los recursos que no son punteros?
Es posible usar unique_ptr
para administrar recursos que no son punteros. Esto se hace implementando una clase de deleter personalizada que proporciona:
- Un
typedef {TYPE} pointer;
donde{TYPE}
es el tipo de recurso no puntero -
operator()(pointer)
que libera el recurso controlado
... y luego crear una instancia de unique_ptr
con el eliminador personalizado como el segundo parámetro de la plantilla.
Por ejemplo, bajo Windows es posible crear un unique_ptr
que administra un controlador de control de servicio . Este tipo de identificador no se libera llamando a delete
, sino llamando a 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 utilizar shared_ptr
para controlar también un recurso no puntero?
De acuerdo con la documentación, hay sobrecargas de constructor shared_ptr
que proporcionan los medios para proporcionar una clase de deleter personalizada, pero ninguno de los constructores acepta un tipo de recurso que no sea un puntero o una envoltura alrededor de un puntero.
¿Cómo se puede hacer esto?
Bueno, shared_ptr invocará el destructor del apuntado al objeto tan pronto como se libere la última referencia al puntero, entonces cualquier cosa que contenga la clase puede liberarse. Solo haz una clase tal 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;
};
Luego simplemente haga un puntero compartido con un nuevo SvcHandle. La gestión de por vida del identificador irá con shared_ptr - RAII en su máxima expresión.
Lamentablemente, la necesidad de shared_ptr
para el borrado de tipo hace que sea imposible con la interfaz actual lograr exactamente lo que desea. unique_ptr
logra hacer eso porque tiene información estática sobre el tipo de eliminador real, desde donde puede dibujar el tipo de "puntero" real. En el caso de shared_ptr, el tipo de eliminación se pierde en el proceso de borrado de tipo (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
(por ejemplo, template<class Y> shared_ptr(Y* p)
). No puede hacerlo porque el pointer
no es necesariamente un tipo de puntero real, y por lo tanto no puede restringir lo que puede aceptarse (excepto quizás a través de algún SFINAE con std::is_convertible_to
o algo así ... pero estoy divagando).
Ahora, una solución obvia es simplemente un new
manejo del recurso, por más 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; });
Piensa no Desde estándar proporcionan tales constructores para shared_ptr y no 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 hacer por ejemplo (para unique_ptr)
pointer release() noexcept;
1 Postcondición: get () == nullptr. 2 Devoluciones: El valor get () tenía al inicio de la llamada a liberar.
¿Sin recurso de puntero? Intentas hackear el lenguaje. Siempre es mala idea.