2012-08-10 26 views
15

Duplicar posibles:
Is it better in C++ to pass by value or pass by constant reference?¿Por qué pasar un objeto por valor en C++

Soy consciente de las diferencias de paso por valor, puntero y referencia en C++ y Considero que pasar objetos por valor (en lugar de referencia constante) en C++ es casi siempre un error de programación.

void foo(Obj o); ... // Bad 

void foo(const Obj &o); ... // Better 

El único caso que puedo pensar en que podría ser apropiado para pasar por valor en lugar de referencia const es donde el objeto es menor que una referencia, y que pasa por el valor es por lo tanto más eficiente.

Pero, seguramente este es el tipo de cosas que los compiladores están diseñados para determinar?

¿Por qué C++ realmente necesita pasa por valor Y pasa por referencia constante, y - ¿pueden los compiladores convertir automáticamente la llamada a (y desde) una referencia constante si corresponde?

(Parece que hay 100s de C++ llamada cuestión de convención, preguntando acerca de las diferencias entre (por ejemplo) el valor y referencia - pero no pude encontrar uno que le preguntó "¿por qué?".)

+2

¿Qué pasa cada vez que necesita una copia de todos modos? Añadir 'Obj o2 (o);' como primera línea? Eso parece bastante inútil. Además, mueve la semántica. – delnan

+0

@delnan - Gracias. Normalmente hago lo que describes, ya que creo que es un detalle de implementación de la función que no debe formar parte de su firma externa. – Roddy

+0

@Nemo. Si gracias. ¡Votado para cerrar! Lamentablemente, la mente de colmena es una herramienta de búsqueda mejor que la incorporada de SO ... – Roddy

Respuesta

4

Si que quería hacer las cosas en el objeto dentro de la función sin afectar el original, me paso por valor:

A minus(A b){ 
    b.val=-b.val; 
    return b; 
} 
1

Si el objeto es mutable, pasando por el valor da el receptor de su propia copia de usar y donde el cambio sensato , sin afectar la copia de la persona que llama - siempre asumiendo que es una copia suficientemente profunda.

Esto puede simplificar el pensamiento en algunas situaciones de subprocesos múltiples.

10

La cuestión de cuándo pasar por valor puede ser mejor que por referencia de referencia tiene diferentes respuestas con diferentes versiones de la norma.

En el buen viejo C++ 03, y hace unos años, la recomendación sería pasar cualquier cosa que no cabe en un registro por referencia de referencia. En este caso, la respuesta sería:

  • Debido Obj encaja en un registro y que pasan por valor y paso por valor será más eficiente

Todavía en C++ 03, en los últimos años (absurdo como parece que algunos artículos recomendado este casi 10 años atrás, pero no hubo consenso real),

  • si la función necesita para hacer una copia, a continuación, hacerlo en la interfaz permite al compilador por formulario copia-elisión si la fuente de la copia es temporal, por lo que puede ser más eficiente.

Con la aprobación del nuevo estándar de C++ 11, y aumentar el apoyo del compilador para rvalue referencias, en muchos casos, incluso cuando la copia no puede ser elidido, y de nuevo

  • si el función necesita para hacer una copia, incluso cuando la copia no puede ser elidido, y para los tipos que lo apoyan, el contenido será trasladó (en la jerga común del objeto se moverá, pero es sólo el contenido que obtener desplazado), que de nuevo lo hará ser más eficiente que copiar internamente.

A partir de la pregunta de por qué las dos convenciones de llamadas diferentes, tienen diferentes objetivos. Pasar por valor permite que la función modifique el estado del argumento sin interferir con el objeto fuente. Además, el estado del objeto fuente tampoco interferirá con la función (considere un entorno multiproceso y un hilo que modifique la fuente mientras la función todavía se está ejecutando).

1

¿Por qué C++ realmente necesita pasar por valor Y pasar por referencia constante, y - ¿pueden los compiladores convertir automáticamente la llamada a (y desde) una referencia constante si corresponde?

Déjame contestar la primera primero: a veces.

Los compiladores pueden omitir la copia en el parámetro, pero solo si transfiere un valor r temporalmente. Por ejemplo:

void foo(Obj o); 

foo((Obj()))); //Extra set of parenthesis are needed to prevent Most Vexing Parse 

La copia del temporal en el parámetro argumento puede ser elidido (es decir: no se copiarán), según la conveniencia del compilador).

Sin embargo, nunca se va a se elide esta copia:

Obj a; 
foo(a); 

Ahora, volviendo a la primera. C++ necesita ambos porque es posible que desee usar ambos para cosas diferentes. Pasar por valor es útil para transferir la propiedad; esto es más importante en C++ 11 donde podemos mover en lugar de copiar objetos.

+0

Gracias. "Pasar de valor es útil para transferir la propiedad" ¿Podría elaborar un poco? – Roddy

3

El modismo de intercambio de copia usa pasar por valor para lograr una copia generada por el compilador.

MyClass& operator=(MyClass value) // pass by value to generate copy 
{ 
    value.swap(*this);   // Now do the swap part. 
    return *this; 
} 

Básicamente en situaciones en las que deberá modificar el parámetro pero no desea tocar el original. En estas situaciones, si pasa por la referencia de referencia, manualmente debe crear una copia dentro de la función. Estos pasos manuales evitarán ciertas optimizaciones que el compilador puede realizar si permite que el compilador maneje la copia.

MyClass a; 
// Some code 
a = MyClass(); // reset the value of a 
       // compiler can easily elide this copy. 
6

Sin duda una de las razones C++ tiene el paso por valor se debe a que la heredó de C, y la eliminación de que podría romper el código de poca ganancia.

En segundo lugar, como se señala, para los tipos que son más pequeños que una referencia que pasa por valor sería menos eficiente.

Otro caso menos obvio, sin embargo es si usted tiene una función que necesita una copia de su argumento, por alguna razón:

void foo(const Obj& obj) 
{ 
    if(very_rare_check()) return; 

    Obj obj_copy(obj); 
    obj_copy.do_work(); 
} 

En este caso la nota que se está forzando una copia. Pero supongamos que se llama a esta función con el resultado de otra función que devuelve por valor:

Obj bar() { return Obj(parameters); } 

y lo llaman así: foo(bar());

Ahora cuando se utiliza la versión de referencia const, el compilador terminar haciendo dos objetos: el temporal y la copia en foo. Sin embargo, si pasó por valor, el compilador puede optimizar todos los temporales en la ubicación utilizada por el parámetro de valor por valor foo.

Hay un gran artículo sobre esto y seguir la semántica en general en http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

Finalmente la forma canónica para implementar ciertos operadores es utilizar el paso por valor para evitar copias dentro del operador:

Obj operator+(Obj left, const Obj& right) 
{ 
    return left += right; 
} 

Observe cómo esto permite que el compilador genere la copia en el parámetro en lugar de forzar una copia u objeto temporal dentro del código del operador.

+0

+1 para el artículo "¿Quiere velocidad? Pase por valor" –

Cuestiones relacionadas