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.

¿Fue útil?

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 < > 'aliasing' es de un documento WG21, " Mejora 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   instancia p 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ábrica make_shared   más adelante en este documento también puede ser   implementado utilizando solo el público   interfaz de shared_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 almacena p y comparte la propiedad con r .

         

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 de r 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 a shared_ptr < U > siempre que T *   se puede convertir implícitamente a U *. En   en particular, shared_ptr < T > es   Convertible implícitamente a shared_ptr < T > const ,   a shared_ptr < U > donde U es un   base accesible de T, y para    shared_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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top