2009-12-25 17 views
5

Considerando el ejemplo anterior, no pudimos modificar la variable de paso por referencia, tampoco pudimos modificar la variable de paso por valor.La salida de cada método es la misma.Puesto que no hay diferencia entre estos dos método, ¿por qué C++ admite ambos métodos?Cualquier diferencia entre f (const string &) yf (const string)?

gracias.

+2

Relacionados http://stackoverflow.com/questions/1567138/const-t-arg-vs-t-arg/1567186#1567186 –

Respuesta

19

Hay una diferencia entre los dos. Considere lo siguiente:

#include <iostream> 
#include <string> 
using std::string; 

string g_value; 

void callback() { 
    g_value = "blue"; 
} 

void ProcessStringByRef(const string &s) { 
    callback(); 
    std::cout << s << "\n"; 
} 

void ProcessStringByValue(const string s) { 
    callback(); 
    std::cout << s << "\n"; 
} 

int main() { 
    g_value = "red"; 
    ProcessStringByValue(g_value); 
    g_value = "red"; 
    ProcessStringByRef(g_value); 
} 

Salida:

red 
blue 

El hecho de que una referencia es const dentro de una función, no significa que el referand no se puede modificar a través de otras referencias (la situación de uno el objeto que tiene múltiples referencias o punteros se llama "aliasing"). Por lo tanto, hay una diferencia entre pasar una referencia constante y pasar un valor constante: en el caso de la referencia, el objeto puede cambiar después de que se realiza la llamada. En el caso del valor, el destinatario tiene una copia privada, que no cambiará.

Dado que hacen cosas diferentes, C++ le permite elegir cuál desea.

Existen consecuencias para el rendimiento en ambos sentidos: cuando se pasa por valor, se debe realizar una copia, lo que cuesta. Pero el compilador sabe entonces que solo su función puede tener referencias a esa copia, lo que podría permitir otras optimizaciones. ProcessStringByRef no puede cargar el contenido de la cadena para imprimir hasta que callback() haya regresado. ProcessStringByValue puede, si el compilador cree que hacerlo es más rápido.

Por lo general, le importa la copia, no el orden de ejecución de las instrucciones, porque generalmente la copia es mucho más cara. Por lo general, pasa por referencia donde sea posible para objetos que no son triviales para copiar. Pero la posibilidad de aliasing a veces tiene consecuencias muy graves para el rendimiento, ya que impide ciertas optimizaciones, incluso aunque no se produzca aliasing. Es por eso que existen "reglas de aliasing estrictas" y la palabra clave restrict en C99.

1

Con outputStringByRef debe asegurarse de que la variable a la que hace referencia le quede viva y sin cambios durante el tiempo que outputStringByRef lo necesite. con outputStringByVal la variable que aprobó puede morir o quedar fuera del alcance y la copia que la función tiene todavía está bien.

8

f(const string&) toma de cadena por const referencia: f está operando directamente en el objeto de cadena pasado por referencia: no hay copia involucrada. const impide modificaciones al objeto original sin embargo.

f(const string) toma un valor de cadena, lo que significa que f recibe una copia de la cadena original. Incluso si elimina const, al pasar por valor, cualquier modificación de la cadena se perderá cuando f regrese.

No sé exactamente qué quiere decir con "¿por qué C++ admite ambos métodos?". Solo se aplican reglas generales de sobrecarga.

+0

'¿Por qué C++ admite ambos métodos?, Porque al principio, no vi la diferencia entre estas dos funciones. Pensé que las dos son las mismas. Después de excavar en el conjunto g ++ y msvc generado, parece más o menos lo mismo al pasar la misma dirección de la constante. – Jichao

3

el objeto podría contener un miembro mutable, que se puede modificar incluso con una referencia constante

6

f(string s) paso por valor la cadena s, en otras palabras, se crea una copia y lo inicializa con el valor de la cadena que pasar. Cualquier cambio en la copia, no se propagará a la cadena original que pasó para llamar a la función. En f(const string s) const es redundante porque de todos modos no puede cambiar el valor original.

En lugar de f(const string& s), la cadena no se copia pero le pasa una referencia. Esto generalmente se hace cuando tienes un objeto grande, por lo que el "valor de paso" puede generar una sobrecarga (es por eso que C++ admite ambos métodos). Pasar por referencia significa que puede cambiar el valor del objeto "grande" que pasa, pero debido al especificador const, no puede modificarlo. Es una especie de "protección".

+1

'const string s' puede ser necesaria si es mejor transmite el intento – philsquared

+0

¿Cuál es la diferencia en la intención entre' string s' y 'const string s'? –

+0

Martin B: es un detalle interno de la función y puede ayudar a quien tenga que escribir esa función, pero las firmas son compatibles (puede hacer 'void f (int const); typedef void (* FP) (int); FP p = & f; ') y no tiene significado para los llamantes. –

1

Hay (casi) ninguna diferencia en términos de poder modificar la cadena dentro de la función. Sin embargo, hay una gran diferencia en términos de lo que se está pasando. La sobrecarga const mystring &ss toma una referencia constante a la cadena. Aunque no puede modificarlo, es la misma memoria que se aborda. Si la cadena es larga, esto puede ser un factor importante (suponiendo que la cadena no se implemente usando copy-on-write). El formulario const mystring ss está haciendo una copia de la cadena, por lo que se abordarán diferentes memorias.

En realidad, la forma const mystring &ss podría cambiar la cadena, si se utilizó un const_cast<mystring&> - aunque yo no recomendaría que aquí.

1

Imagínese que desea imprimir un objeto que no se puede copiar:

Thread th; 
// ... 
cout << th; // print out informations... 

La versión de referencia no copiará el hilo, pero tendrá la dirección de th y crear un alias a la misma.La otra versión trataría de copiar el hilo al pasar por valor, pero la copia puede no ser útil para dicho objeto (¿habría entonces un hilo adicional?).

Puede ayudar a pensar de copiar un objeto como clonar un objeto. Copiar th arriba en C++ no significa meramente tener otro control para el mismo objeto que en Java - significa clonar implícitamente el objeto denotado y tener una copia completa del mismo. Entonces, la pregunta es similar a "¿Por qué Java admite tanto Object.clone como copia de referencias?" - ambos tienen diferentes propósitos.

Y luego hay una cuestión de rendimiento también, después de todo. No desea copiar todas y cada una de las veces que pasa algo por objetos hambrientos de recursos.

Cuestiones relacionadas