valor shared_ptr referencia por paso pase parametros llamadas funcion example ejercicios c++ c++11 boost shared-ptr

c++ - paso - ¿Debemos pasar un shared_ptr por referencia o por valor?



paso por valor y por referencia c++ (8)

Cuando una función toma un shared_ptr (desde boost o C ++ 11 STL), ¿la está pasando?

  • por referencia const: void foo(const shared_ptr<T>& p)

  • o por valor: void foo(shared_ptr<T> p) ?

Preferiría el primer método porque sospecho que sería más rápido. ¿Pero realmente vale la pena o hay algún problema adicional?

¿Podría por favor explicar las razones de su elección o, si es el caso, por qué cree que no importa?


Aquí está la toma de Herb Sutter

Guía: No pase un puntero inteligente como parámetro de función a menos que desee utilizar o manipular el puntero inteligente, como compartir o transferir la propiedad.

Pauta: Exprese que una función almacenará y compartirá la propiedad de un objeto de pila utilizando un parámetro shared_ptr por valor.

Pauta: use un parámetro shared_ptr & no solo constante para modificar el shared_ptr. Use una variable shared_ptr y como parámetro solo si no está seguro de si tomará o no una copia y compartirá la propiedad; de lo contrario, use el widget * en su lugar (o, si no es anulable, un widget y).


Desde C ++ 11, debe tomarlo por valor sobre const y más a menudo de lo que piensa.

Si está tomando std :: shared_ptr (en lugar del tipo subyacente T), entonces lo está haciendo porque quiere hacer algo con él.

Si desea copiarlo en algún lugar, tiene más sentido tomarlo por copia y std :: moverlo internamente, en lugar de hacerlo por const y luego copiarlo. Esto se debe a que le permite a la persona que llama la opción a su vez std :: mover el shared_ptr al llamar a su función, con lo que se ahorra un conjunto de operaciones de incremento y decremento. O no. Es decir, el que llama a la función puede decidir si necesita o no el std :: shared_ptr después de llamar a la función, y dependiendo de si se mueve o no. Esto no se puede lograr si se pasa por const y, por lo tanto, es preferible tomarlo por valor.

Por supuesto, si la persona que llama necesita su shared_ptr por más tiempo (por lo tanto no puede std :: moverlo) y no desea crear una copia simple en la función (digamos que desea un puntero débil, o solo a veces desea para copiarlo, dependiendo de alguna condición), entonces puede ser preferible una constante y una constante.

Por ejemplo, deberías hacer

void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));

terminado

void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);

Porque en este caso siempre creas una copia internamente.


Esta pregunta ha sido discutida y respondida por Scott, Andrei y Herb durante la sesión Ask Us Anything en C ++ y más allá de 2011 . Míralo desde 4min 34seg en shared_ptr rendimiento y corrección .

En breve, no hay razón para pasar por valor , a menos que el objetivo sea compartir la propiedad de un objeto (por ejemplo, entre diferentes estructuras de datos o entre diferentes hilos).

A menos, puede moverlo y optimizarlo como lo explica Scott Meyers en el video de conversación vinculado anteriormente, pero eso está relacionado con la versión real de C ++ que puede usar.

Una actualización importante de esta discusión ha ocurrido durante el Panel Interactivo de la conferencia de GoingNative 2012 : ¡Pregúntanos algo! que vale la pena ver, sobre todo a partir de las 22:50 .


Pasar por referencia const , es más rápido. Si necesita almacenarlo, digamos en algún contenedor, la ref. el conteo se incrementará automáticamente mágicamente por la operación de copia.


Personalmente usaría una referencia const . No es necesario incrementar el recuento de referencia solo para disminuirlo nuevamente por el bien de una llamada de función.


Sin saber el costo de tiempo de la operación de copia shared_copy donde está el incremento atómico y la disminución, sufrí un problema de uso de la CPU mucho mayor. Nunca esperé que el incremento y decremento atómico pueda costar tanto.

Tras el resultado de mi prueba, el incremento y decremento atómico int32 toma 2 o 40 veces más que el incremento y decremento no atómico. Lo tengo en 3GHz Core i7 con Windows 8.1. El primer resultado sale cuando no se produce una disputa, el último cuando se produce una alta posibilidad de contienda. Tengo en cuenta que las operaciones atómicas son, por último, un bloqueo basado en hardware. Bloqueo es bloqueo Mala para el rendimiento cuando se produce la contención.

Experimentando esto, siempre uso byref (const shared_ptr &) que byval (shared_ptr).


shared_ptr no es lo suficientemente grande, ni su constructor / destructor hace el trabajo suficiente para que haya suficiente sobrecarga de la copia para preocuparse de pasar por referencia y de pasar por rendimiento de copia.


shared_ptr el siguiente código, una vez con foo tomando el shared_ptr by const& y otra vez con foo tomando el shared_ptr por valor.

void foo(const std::shared_ptr<int>& p) { static int x = 0; *p = ++x; } int main() { auto p = std::make_shared<int>(); auto start = clock(); for (int i = 0; i < 10000000; ++i) { foo(p); } std::cout << "Took " << clock() - start << " ms" << std::endl; }

Uso de VS2015, versión x86, en mi procesador Intel Core 2 Quad (2.4GHz)

const shared_ptr& - 10ms shared_ptr - 281ms

La versión copia por valor fue un orden de magnitud más lenta.
Si está llamando a una función sincrónicamente desde el subproceso actual, prefiera const& version.