Si declaro un objeto envuelto en un puntero compartido:
std::shared_ptr<myClass> myClassObject(new myClass());
luego quería pasarlo como argumento a un método:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
¿Lo anterior simplemente incrementa el recuento de referencias de shared_pt y todo está bien? ¿O deja un puntero colgando?
¿Todavía se supone que debes hacer esto ?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Creo que la segunda forma puede ser más eficiente porque solo tiene que copiar 1 dirección (a diferencia de todo el puntero inteligente), pero la primera forma parece más legible y no anticipo empujar los límites de rendimiento. Solo quiero asegurarme de que no haya nada peligroso al respecto.
Gracias.
c++
c++11
shared-ptr
c++-faq
Steve H
fuente
fuente
const std::shared_ptr<myClass>& arg1
DoSomething
realmente es necesario compartir la propiedad? Parece que debería tomar una referencia en su lugar ...void DoSomething(myClass& arg1)
.shared_ptr<>
específicamente, debe pasar por valor para poder compartir la propiedad.std::make_shared
no solo es más eficiente, sino también más seguro que elstd::shared_ptr
constructor.Respuestas:
Claro, puedo ayudarte con eso. Supongo que tiene cierta comprensión de la semántica de propiedad en C ++. ¿Es eso cierto?
Bueno.
Ok, solo puedo pensar en dos razones para
shared_ptr
argumentar:shared_ptr
s.Cual te interesa?
Ejemplos de tales funciones incluyen
std::static_pointer_cast
comparadores personalizados o predicados. Por ejemplo, si necesita encontrar todos los shared_ptr únicos de un vector, necesita dicho predicado.Exactamente.
Si. Y si no cambia el puntero, desea pasar por referencia constante. No es necesario copiar, ya que no es necesario compartir la propiedad. Ese es el otro escenario.
¿En el que compartes la propiedad? Okay. ¿Cómo se comparte la propiedad con
shared_ptr
?Entonces la función necesitará hacer una copia de a
shared_ptr
, ¿correcto?No, eso es una pesimización. Si se pasa por referencia, la función no tendrá más remedio que realizar la copia manualmente. Si se pasa por valor, el compilador elegirá la mejor opción entre una copia y un movimiento y la realizará automáticamente. Entonces, pase por valor.
La función puede simplemente mover el
shared_ptr
argumento a su almacenamiento. Mover unshared_ptr
es barato porque no cambia ningún recuento de referencias.En ese caso,
shared_ptr
es completamente irrelevante para la función. Si desea manipular el pointee, tome un pointee y deje que las personas que llaman elijan qué semántica de propiedad quieren.Se aplican las reglas habituales. Los punteros inteligentes no cambian nada.
Derecha.
Ah, un caso extremo interesante. No espero que eso suceda a menudo. Pero cuando suceda, puede pasar por valor e ignorar la copia si no la necesita, o pasar por referencia y hacer la copia si la necesita.
Si se encuentra en una situación en la que eso realmente importa, puede proporcionar dos sobrecargas, una tomando una referencia de valor constante y otra tomando una referencia de valor r. Uno copia, el otro se mueve. Una plantilla de función de reenvío perfecto es otra opción.
fuente
shared_ptr<T> ptr;
(es decirvoid f(T&)
, llamado conf(*ptr)
) y el objeto no sobrevive a la llamada. Alternativamente, escriba uno en el que pase por el valorvoid f(T)
y los problemas.void f(T&)
hilos y los involucra. Creo que mi problema es que el tono de su respuesta desalienta la transferencia de propiedad de shared_ptr's, que es la forma más segura, intuitiva y menos complicada de usarlos. Estaría extremadamente en contra de aconsejar a un principiante que extraiga una referencia a los datos propiedad de un shared_ptr <>, las posibilidades de uso indebido son grandes y el beneficio es mínimo.Creo que la gente tiene miedo innecesariamente de usar punteros sin procesar como parámetros de función. Si la función no va a almacenar el puntero o afectará su vida útil, un puntero sin formato funciona igual de bien y representa el mínimo común denominador. Considere, por ejemplo, cómo pasaría a
unique_ptr
a una función que toma ashared_ptr
como parámetro, ya sea por valor o por referencia constante.void DoSomething(myClass * p); DoSomething(myClass_shared_ptr.get()); DoSomething(myClass_unique_ptr.get());
Un puntero sin formato como parámetro de función no le impide usar punteros inteligentes en el código de llamada, donde realmente importa.
fuente
DoSomething(*a_smart_ptr)
fun(&x)
afun(x)
. En el último ejemplo,x
podría pasarse por valor o como una ref constante. Como se indicó anteriormente, también le permitirá pasarnullptr
si no está interesado en la salida ...Sí, la idea completa sobre un shared_ptr <> es que varias instancias pueden contener el mismo puntero sin procesar y la memoria subyacente solo se liberará cuando se destruya la última instancia de shared_ptr <>.
Evitaría un puntero a shared_ptr <> ya que eso anula el propósito, ya que ahora está tratando con raw_pointers nuevamente.
fuente
Pasar por valor en su primer ejemplo es seguro, pero hay un modismo mejor. Pase por la referencia constante cuando sea posible; yo diría que sí incluso cuando se trata de punteros inteligentes. Su segundo ejemplo no está exactamente roto pero es muy
!???
. Tonto, no logra nada y derrota parte del punto de los punteros inteligentes, y te va a dejar en un mundo lleno de errores cuando intentas desreferenciar y modificar las cosas.fuente
shared_ptr<>
específicamente, que debe hacer una copia para poder compartir la propiedad; si tiene una referencia o puntero a un,shared_ptr<>
entonces no está compartiendo nada y está sujeto a los mismos problemas de por vida que una referencia o puntero normal.shared_ptr
garantiza que el original de la persona que llama durará más que la llamada a la función, por lo que la función es segura al usarshared_ptr<> const &
. Si necesita almacenar el puntero para un momento posterior, tendrá que copiar (en este punto se debe hacer una copia, en lugar de guardar una referencia), pero no hay necesidad de incurrir en el costo de la copia a menos que necesite hacerlo. eso. Yo iría por pasar una referencia constante en este caso ....shared_ptr
de la interfaz y pasar unaconst
referencia simple ( ) al objeto puntiagudo.shared_ptr<>
para empezar? Ahora su API es realmente solo un dolor de cabeza, ya que obliga a la persona que llama a cambiar inútilmente la semántica de la vida útil de su objeto solo para usarla. No veo nada que valga la pena defender en eso, aparte de decir "técnicamente no daña nada". No veo nada controvertido sobre 'si no necesita la propiedad compartida, no la useshared_ptr<>
'.en su función
DoSomething
, está cambiando un miembro de datos de una instancia de clase,myClass
por lo que lo que está modificando es el objeto administrado (puntero sin procesar), no el objeto (shared_ptr). Lo que significa que en el punto de retorno de esta función, todos los punteros compartidos al puntero sin procesar administrado verán su miembro de datos:myClass::someField
cambiado a un valor diferente.en este caso, está pasando un objeto a una función con la garantía de que no lo está modificando (hablando de shared_ptr, no del objeto de propiedad).
El modismo para expresar esto es a través de: una referencia constante, así
void DoSomething(const std::shared_ptr<myClass>& arg)
Asimismo, le está asegurando al usuario de su función que no está agregando otro propietario a la lista de propietarios del puntero sin procesar. Sin embargo, mantuvo la posibilidad de modificar el objeto subyacente apuntado por el puntero sin formato.
AVISO: Lo que significa que, si de alguna manera alguien llama
shared_ptr::reset
antes de que usted llame a su función, y en ese momento es el último shared_ptr que posee el raw_ptr, entonces su objeto será destruido y su función manipulará un puntero colgando al objeto destruido. ¡¡¡MUY PELIGROSO!!!fuente