2009-09-10 11 views
26

boost::shared_ptr tiene un constructor inusual¿Para qué se utiliza shared_ptr (shared_ptr <Y> const & r, T * p) de boost?

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

y estoy un poco perplejo en cuanto a lo que esto sería útil para. Básicamente comparte la propiedad con r, pero .get() devolverá p. nor.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á esto:

0x8c66008 
0x8c66030 
2 
2 

Tenga en cuenta que los punteros están separados, pero ambos dicen tener un use_count de 2 (dado que comparten la propiedad del mismo objeto).

Por lo tanto, la propiedad de intx existirán siempre y cuando xoy es de alrededor. Y si entiendo los documentos correctos, el segundo int nunca se destruye. He confirmado esto 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; 
} 

Este salidas (como se esperaba):

T() 
T() 
0x96c2008 
0x96c2030 
2 
2 
~T() 

Entonces ... ¿cuál es la utilidad de este constructo inusual que comparte la propiedad de un puntero , pero actúa como como otro puntero (que no posee) cuando se usa.

+7

Buena pregunta. +1 – GManNickG

+3

TL; Versión DR: crea un puntero a un subobjeto de 'r'. –

Respuesta

29

Es útil cuando se desea compartir un miembro de la clase y una instancia de la clase es ya un shared_ptr, como la siguiente:

struct A 
{ 
    int *B; // managed inside A 
}; 

shared_ptr<A> a(new A); 
shared_ptr<int> b(a, a->B); 

comparten el recuento de uso y esas cosas. Es optimización para uso de memoria.

+1

una buena respuesta. claramente, en este ejemplo, querríamos mantener el objeto 'a' mientras que' b' esté alrededor. Creo que tenemos un ganador. –

+0

No solo una optimización para el uso de memoria, sino en el ejemplo específico, usar un enfoque diferente terminaría con una llamada a 'eliminar (a-> B)' que puede ser inesperada (considere 'struct A {int b;}; shared_ptr a (nuevo A); shared_ptr b (a, & a-> b) ') –

2

Puede tener un puntero a algún controlador o una estructura de datos de 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 la cuenta de uso pero devolver los datos adicionales si el primer puntero posee los otros punteros de datos.

8

Para ampliar leiz's y piotr's respuestas, esta descripción de shared_ptr<> 'aliasing' es de un papel WG21, "Improving shared_ptr for C++0x, Revision 2":

III. Aliasing Soporte

Los usuarios avanzados a menudo requieren la capacidad para crear un shared_ptr ejemplo p que comparte la propiedad con otra (maestro) shared_ptrq pero puntos 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 constructor adicional que se puede utilizar para este propósito .

Un efecto secundario interesante de este aumento de la potencia expresiva es que ahora los *_pointer_cast funciones pueden ser implementadas en código de usuario. La función de la fábrica make_shared presentó más adelante en este documento también puede ser implementado utilizando únicamente el interfaz pública de shared_ptr a través del constructor aliasing.

Impacto:

Esta característica se extiende la interfaz de shared_ptr en una compatible con versiones anteriores manera que aumenta su expresivo potencia y, por tanto, es fuertemente recomendado para ser añadido a la 0x C++ estándar . No presenta problemas de compatibilidad binaria con fuente y .

Texto propuesto:

Añadir a shared_ptr [util.smartptr.shared] la siguiente constructor:

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

Agregue lo siguiente al [util.smartptr. shared.const]:

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

Efectos: Crea una instancia shared_ptr que almacena p y comparte la propiedad conr.

Postcondiciones:get() == p && use_count() == r.use_count().

Tira: nada.

[Nota: Para evitar la posibilidad de una referencia colgante, el usuario de este constructor debe asegurarse de que p sigue siendo válida al menos hasta que el grupo propiedad de r se destruye. --end nota]

[Nota:. Este constructor permite la creación de una instancia vacía shared_ptr con un puntero no nulo almacenado. --end nota.]

4

También puede utilizar esto para mantener los punteros emitidos dinámicos, es decir,:

class A {}; 
class B: public A {}; 

shared_ptr<A> a(new B); 
shared_ptr<B> b(a, dynamic_cast<B*>(a.get())); 
+0

otro uso interesante que claramente cae en la categoría de "aliasing". Buen punto. –

0

Por "shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));"

creo que no es la forma recomendada utilizando puntero inteligente.

La forma recomendada de hacer esta conversión de tipo debe ser:

shared_ptr<B> b(a); 

Dado que en el documento Boost se menciona que:

shared_ptr<T> puede ser implícita convertidos a shared_ptr<U> siempre que T * puede ser implícitamente convertido a U *. En particular, , shared_ptr<T> es convertir implícitamente a shared_ptr<T> const, a shared_ptr<U> donde U es un punto de acceso de T, y para shared_ptr<void>.

Además de eso, también tenemos dynamic_pointer_cast que podría hacer directamente la conversión en objeto puntero inteligente y ambos de estos dos métodos sería mucho más seguro que la forma del puntero en bruto de fundición de forma manual.

0

he puesto constructor de aliasing de shared_ptr en uso en mi pequeña biblioteca:

http://code.google.com/p/infectorpp/ (sólo mi sencilla contenedor COI)

El punto es que, dado que necesitaba un shared_ptr de tipo conocido que se volvió de una clase polimórfica (que no conoce el tipo). No pude convertir implícitamente el shared_ptr al tipo que necesitaba.

En el archivo "InfectorHelpers.hpp" (línea 72-99) puede ver que en acción para el tipo IAnyShared.

Aliasing constructor crea shared_ptr que no elimina los punteros en realidad están apuntando a, pero que todavía aumentar el contador de referencia al objeto original y que puede ser tremendamente útil.

Básicamente puedes crear un puntero a cualquier cosa usando un constructor de aliasing 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 tanto "un contador de referencia" y un puntero a una istance de T, datos suficientes para crear algo con el constructor aliasing

std::shared_ptr<T> aPtr(any->getReferenceCounter(), //share same ref counter 
       static_cast<T*>(any->getPtr())); //potentially unsafe cast! 

No pretendo haber inventado este usar para el constructor de aliasing, pero nunca he visto a otra persona haciendo lo mismo. Si está adivinando si ese código sucio funciona, la respuesta es sí.

Cuestiones relacionadas