¿Para qué se utiliza el recurso shared_ptr (shared_ptr < Y > const & amp; r, T * p)?
-
05-07-2019 - |
Pregunta
boost :: shared_ptr
tiene un constructor inusual
template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p);
y estoy un poco desconcertado sobre para qué sería útil esto. Básicamente comparte la propiedad con r
, pero .get ()
devolverá p
. no r.get ()
!
Esto significa que puede hacer algo como esto:
int main() {
boost::shared_ptr<int> x(new int);
boost::shared_ptr<int> y(x, new int);
std::cout << x.get() << std::endl;
std::cout << y.get() << std::endl;
std::cout << x.use_count() << std::endl;
std::cout << y.use_count() << std::endl;
}
Y obtendrás esto:
0x8c66008
0x8c66030
2
2
Tenga en cuenta que los punteros están separados, pero ambos afirman tener un use_count
de 2 (ya que comparten la propiedad del mismo objeto).
Por lo tanto, int
propiedad de x
existirá siempre que x
o y está alrededor. Y si entiendo que los documentos son correctos, el segundo
int
nunca se destruye. Lo he confirmado con el siguiente programa de prueba:
struct T {
T() { std::cout << "T()" << std::endl; }
~T() { std::cout << "~T()" << std::endl; }
};
int main() {
boost::shared_ptr<T> x(new T);
boost::shared_ptr<T> y(x, new T);
std::cout << x.get() << std::endl;
std::cout << y.get() << std::endl;
std::cout << x.use_count() << std::endl;
std::cout << y.use_count() << std::endl;
}
Esto produce (como se espera):
T()
T()
0x96c2008
0x96c2030
2
2
~T()
Entonces ... cuál es la utilidad de esta construcción inusual que comparte la propiedad de un puntero, pero actúa como otro puntero (que no posee) cuando se usa.
Solución
Es útil cuando desea compartir un miembro de la clase y una instancia de la clase ya es un shared_ptr, como el siguiente:
struct A
{
int *B; // managed inside A
};
shared_ptr<A> a( new A );
shared_ptr<int> b( a, a->B );
comparten la cuenta de uso y esas cosas. Es la optimización para el uso de memoria.
Otros consejos
Para ampliar en leiz's y respuestas de piotr , esta descripción de shared_ptr
para C ++ 0x, Revisión 2 " :
III. Aliasing Support
Los usuarios avanzados a menudo requieren la capacidad de crear un
shared_ptr
instanciap
que comparte la propiedad con otro (maestro)shared_ptr
q
pero apunta a un objeto que no es una base de* q
.* p
puede ser un miembro o un elemento de* q
, por ejemplo. Esta sección propone un adicional constructor que puede ser utilizado para este propósito.Un efecto secundario interesante de esto El aumento del poder expresivo es que ahora las funciones
* _pointer_cast
pueden implementarse en el código de usuario. los Se presenta la función de fábricamake_shared
más adelante en este documento también puede ser implementado utilizando solo el público interfaz deshared_ptr
a través de constructor de alias.Impact:
Esta característica extiende la interfaz de
shared_ptr
en una versión compatible con versiones anteriores manera que aumenta su expresividad poder y por lo tanto es fuertemente Recomendado para ser agregado al C ++ 0x. estándar. No introduce fuente y Problemas de compatibilidad binaria.Texto propuesto:
Añadir a
shared_ptr
[util.smartptr.shared] lo siguiente constructor:template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );
Agregue lo siguiente a [util.smartptr.shared.const]:
<*>Efectos: Construye una instancia de
shared_ptr
que almacenap
y comparte la propiedad conr .
Condiciones posteriores:
get () == p & amp; & amp; use_count () == r.use_count ()
.Lanza: nada.
[Nota: Para evitar la posibilidad de un puntero colgante, el usuario de este constructor debe garantizar que
p
permanezca válido al menos hasta que el grupo de propietarios der
se destruya. - nota final.][Nota: Este constructor permite la creación de un vacío
shared_ptr
instancia con un puntero no nulo almacenado. - nota final.]
También puede usar esto para mantener punteros dinámicos fundidos, es decir,
class A {};
class B: public A {};
shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
Es posible que tenga un puntero a algún controlador o una estructura de datos de la API de nivel inferior que pueda asignar datos adicionales por su API de nivel inferior u otros medios. En este caso, puede ser interesante aumentar use_count pero devolver los datos adicionales si el primer puntero posee los otros punteros de datos.
He puesto el constructor de aliasing de shared_ptr en uso en mi pequeña biblioteca:
http://code.google.com/p/infectorpp/ (solo mi contenedor simple de IoC)
El punto es que ya que necesitaba un shared_ptr de tipo conocido para ser devuelto desde una clase polimórfica (que no conoce el tipo). No pude convertir implícitamente shared_ptr al tipo que necesitaba.
En el archivo " InfectorHelpers.hpp " (línea 72-99) puede ver eso en acción para el tipo IAnyShared.
El constructor de alias crea shared_ptr que no elimina los punteros a los que realmente apuntan, pero todavía aumentan el contador de referencia al objeto original y eso puede ser tremendamente útil.
Básicamente, puedes crear un puntero a cualquier cosa utilizando el constructor de alias y amenazarlo como un contador de referencia.
//my class
std::shared_ptr<T> ist;
int a; //dummy variable. I need its adress
virtual std::shared_ptr<int> getReferenceCounter(){
return std::shared_ptr<int>(ist,&a); //not intended for dereferencing
}
virtual void* getPtr(); //return raw pointer to T
ahora tenemos ambos "un contador de referencia" y un puntero a un istance de T, datos suficientes para crear algo con el constructor de alias
std::shared_ptr<T> aPtr( any->getReferenceCounter(), //share same ref counter
static_cast<T*>(any->getPtr()) ); //potentially unsafe cast!
No pretendo haber inventado este uso para el constructor de alias, pero nunca vi a alguien más haciendo lo mismo. Si está adivinando si ese código sucio funciona, la respuesta es sí.
Para " shared_ptr < B > b (a, dynamic_cast < B * > (a.get ()))
"
Creo que no es la forma recomendada de usar el puntero inteligente.
La forma recomendada de hacer este tipo de conversión debería ser:
shared_ptr<B> b(a);
Como en el documento Boost se menciona que:
shared_ptr < T >
puede ser implícitamente convertido ashared_ptr < U >
siempre que T * se puede convertir implícitamente a U *. En en particular,shared_ptr < T >
es Convertible implícitamente ashared_ptr < T > const
, ashared_ptr < U >
donde U es un base accesible de T, y parashared_ptr < void >
.
Además de eso, también tenemos dynamic_pointer_cast lo que podría hacer directamente la conversión en el objeto Smart Pointer y ambos métodos serían mucho más seguros que la forma manual de puntero sin formato.