Pasar punteros compartidos como argumentos

Si declaro un objeto envuelto en un puntero compartido:

std::shared_ptr myClassObject(new myClass()); 

entonces quería pasarlo como argumento a un método:

 DoSomething(myClassObject); //the called method void DoSomething(std::shared_ptr 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* arg1) { (*arg1)->someField = 4; } 

Creo que la 2ª vía puede ser más eficiente porque solo tiene que copiar 1 dirección (a diferencia de todo el puntero inteligente), pero la 1ª forma parece más legible y no anticipo que se reduzcan los límites de rendimiento. Solo quiero asegurarme de que no haya algo peligroso al respecto.

Gracias.

Quiero pasar un puntero compartido a una función. ¿Me puede ayudar con eso?

Claro, puedo ayudarte con eso. Supongo que tiene alguna comprensión de la semántica de propiedad en C ++. ¿Es eso cierto?

Sí, estoy razonablemente cómodo con el tema.

Bueno.

Ok, solo puedo pensar en dos razones para tomar un argumento shared_ptr :

  1. La función quiere compartir la propiedad del objeto;
  2. La función realiza alguna operación que funciona específicamente en shared_ptr s.

¿Cuál te interesa?

Estoy buscando una respuesta general, por lo que estoy realmente interesado en ambos. Tengo curiosidad sobre lo que quieres decir en el caso # 2, sin embargo.

Los ejemplos de tales funciones incluyen std::static_pointer_cast , comparadores personalizados o predicados. Por ejemplo, si necesita encontrar todo el shared_ptr exclusivo de un vector, necesita dicho predicado.

Ah, cuando la función realmente necesita manipular el puntero inteligente en sí.

Exactamente.

En ese caso, creo que deberíamos pasar por referencia.

Sí. Y si no cambia el puntero, quiere pasar por referencia constante. No es necesario copiar ya que no necesita compartir la propiedad. Ese es el otro escenario.

Ok lo tengo. Hablemos del otro escenario.

¿En el que comparte la propiedad? De acuerdo. ¿Cómo comparte la propiedad con shared_ptr ?

Al copiarlo.

Entonces la función necesitará hacer una copia de un shared_ptr , ¿correcto?

Obviamente. ¿Entonces lo paso por una referencia a const y copio a una variable local?

No, eso es una pesimismo. Si se pasa por referencia, la función no tendrá más remedio que hacer la copia manualmente. Si se pasa por valor, el comstackdor seleccionará la mejor opción entre una copia y un movimiento y lo realizará de manera automática. Entonces, pasa por valor.

Buen punto. Debo recordar ese artículo ” Want Speed? Pass by Value. ” Con más frecuencia.

Espera, ¿y si la función almacena el shared_ptr en una variable miembro, por ejemplo? ¿No hará eso una copia redundante?

La función simplemente puede mover el argumento shared_ptr a su almacenamiento. Mover un shared_ptr es barato porque no cambia los recuentos de referencia.

Ah, buena idea.

Pero estoy pensando en un tercer escenario: ¿y si no quieres manipular el shared_ptr , ni compartir la propiedad?

En ese caso, shared_ptr es completamente irrelevante para la función. Si desea manipular la punta, tome una punta y deje que las personas que llaman seleccionen la semántica de propiedad que desean.

¿Y debería tomar la punta por referencia o por valor?

Las reglas habituales se aplican. Los indicadores inteligentes no cambian nada.

Pase por valor si voy a copiar, paso por referencia si quiero evitar una copia.

Derecha.

Hmm. Creo que olvidaste otro escenario. ¿Qué sucede si deseo compartir la propiedad, pero solo dependiendo de una determinada condición?

Ah, un caso de borde interesante. No espero que eso ocurra a menudo. Pero cuando sucede, puede pasar por valor e ignorar la copia si no la necesita, o pasar por referencia y hacer la copia si la necesita.

Arriesgo una copia redundante en la primera opción y pierdo un movimiento potencial en la segunda. ¿No puedo comer el pastel y tenerlo también?

Si se encuentra en una situación en la que realmente importa, puede proporcionar dos sobrecargas, una tomando una referencia de const lvalue y otra tomando una referencia de valor razonable. Una copia, la otra mueve. Una plantilla de función de reenvío perfecto es otra opción.

Creo que cubre todos los escenarios posibles. Muchas gracias.

Creo que la gente está innecesariamente asustada de usar punteros crudos como parámetros de función. Si la función no va a almacenar el puntero ni afectar su vida útil, un puntero sin procesar funciona igual de bien y representa el denominador común más bajo. Considere, por ejemplo, cómo pasaría un unique_ptr a una función que toma un shared_ptr como parámetro, ya sea por valor o por referencia const?

 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.

Sí, la idea completa de un shared_ptr <> es que varias instancias pueden contener el mismo puntero sin formato y la memoria subyacente solo se liberará cuando se destruya la última instancia de shared_ptr <>.

Evitaría un puntero a un shared_ptr <>, ya que eso frustraría el propósito, ya que ahora está tratando nuevamente con raw_pointers.

Pasar por valor en su primer ejemplo es seguro, pero hay una mejor expresión idiomática. Pase por la referencia constante cuando sea posible, diría que sí, incluso cuando se trata de punteros inteligentes. ¡Tu segundo ejemplo no está exactamente roto, pero es muy !??? . Tonto, no logrando nada y derrota parte del punto de los indicadores inteligentes, y va a dejarlo en un mundo lleno de errores cuando intenta desreferenciar y modificar las cosas.