Pregunta

class mystring {
 friend ostream& operator<<(ostream &out, const mystring ss) {
        out << ss.s;
        return out;
    }
private:
    string s;
public:
    mystring(const char ss[]) {
        cout << "constructing mystring : " << ss << endl;
        s = ss;
    }
};

void outputStringByRef(const mystring &ss) {
 cout << "outputString(const string& ) " << ss << endl;
}

void outputStringByVal(const mystring ss) {
 cout << "outputString(const string ) " << ss << endl;
}

int main(void) {
    outputStringByRef("string by reference");
    outputStringByVal("string by value");
    outputStringByRef(mystring("string by reference explict call mystring consructor"));
    outputStringByVal(mystring("string by value explict call mystring constructor"));
} ///:~

Teniendo en cuenta el ejemplo anterior, no podríamos modificar la variable pase por referencia, ni podríamos modificar la salida variable.The pase por valor de cada uno de los métodos es same.Since no hay diferencia entre estos dos métodos, ¿por qué C ++ soporta ambos métodos?

gracias.

¿Fue útil?

Solución

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 un objeto que tiene múltiples referencias o punteros a que se llama "aliasing"). Así, hay una diferente entre pasando una referencia const, y pasando un valor const - en el caso de la referencia, el objeto podría cambiar después de que se realizó la llamada. En el caso del valor, el destinatario tiene una copia privada, que no va a cambiar.

Desde que hacen cosas diferentes, C ++ permite elegir el que desee.

Hay consecuencias para el desempeño de cualquier manera - cuando se pasa por valor, una copia tiene que ser hecho, que cuesta. Pero entonces el compilador sabe que sólo su función puede posiblemente tener ninguna referencia a esa copia, lo que podría permitir que otras optimizaciones. ProcessStringByRef no puede cargar el contenido de la cadena para la impresión hasta que ha vuelto callback(). ProcessStringByValue puede, si el compilador piensa que lo hace, es más rápido.

Por lo general, se preocupan por la copia, no el orden de ejecución de las instrucciones, porque por lo general la copia es mucho más caro. Así que por lo general, se pasa por referencia en lo posible para objetos que no son triviales para copiar. Pero la posibilidad de representaciones falsas a veces tiene muy graves consecuencias para el rendimiento, mediante la prevención de ciertas optimizaciones a pesar de que no hay solapamiento se produce realmente. Es por eso que existen "reglas de alias estricto", y la palabra clave restrict en C99.

Otros consejos

f(const string&) toma cadena por referencia const: fis operando directamente sobre el objeto cadena que se pasa por referencia: no hay ninguna copia en cuestión. const impide modificaciones al objeto original aunque.

f(const string) toma un valor de cadena, lo que significa f se le da una copia de la cadena original. Incluso si se le cae const, al pasar por valor, cualquier modificación de la cadena se pierde cuando vuelve f.

No sé exactamente lo que quiere decir con "¿por qué C ++ soporta ambos métodos?". Es simplemente reglas sobrecarga generales que se aplican.

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

En lugar f(const string& s) la cadena no se copia, pero se pasa una referencia a la misma. Esto se hace generalmente cuando se tiene un objeto grande por lo que el "paso-por-valor" puede producir una sobrecarga (por eso C ++ soporta ambos métodos). El paso por referencia significa que se puede cambiar el valor del objeto "grande" que pase, pero debido a la especificador const, no puede modificarlo. Es una especie de "protección".

Su objeto podría contener un miembro de mutable , que se puede modificar incluso con una referencia const

Con outputStringByRef debe asegurarse de que la variable que está pasando una referencia a que queda vivo y sin cambios durante el tiempo que outputStringByRef lo necesita. outputStringByVal con la variable que ha pasado puede morir o salir del alcance y la copia que tiene la función sigue siendo buena.

No es (casi) ninguna diferencia en términos de ser capaz de 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 trate. Si la cadena es larga esto puede ser un factor importante (suponiendo que la cadena no se implementa utilizando copia -on-escritura). La forma const mystring ss está haciendo una copia de la cadena, la memoria de manera diferente se abordará.

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í

Imagine 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 tomará la dirección de th y crear un alias a la misma. La otra versión sería tratar de copiar el hilo cuando pasa por el valor -. Pero la copia puede no ser senseful para un objeto tal (? ¿Habría un hilo adicional a continuación)

Puede ayudar a pensar de copiar un objeto como clonar un objeto . th copiado anteriormente en C ++ no significa simplemente tener otro identificador para el mismo objeto que en Java - que significa para clonar implícitamente el objeto denotado, y tener una copia entera de la misma. Así que la pregunta es similar a "¿Por qué apoya Java tanto Object.clone y la copia de las referencias?" - ambos tienen diferentes propósitos.

Y entonces no es una cuestión de rendimiento también, después de todo. Usted no quiere copiar todos y cada vez que pasa algo en torno a los objetos que consumen muchos recursos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top