2009-04-27 25 views

Respuesta

27

Aquí está la verdad en todos sus casos:

• Retorno por referencia: La llamada de función se puede utilizar como el lado izquierdo de una asignación. p.ej.utilizando la sobrecarga de operadores, si usted tiene el operador [] sobrecargada, se puede decir algo como

a[i] = 7; 

(al devolver por referencia es necesario asegurarse de que el objeto que regrese está disponible después de la vuelta: no se debe devolver una referencia a local o temporal)

• Retorno como valor constante: Impide que la función se use en el lado izquierdo de una expresión de asignación. Considere el operador sobrecargado +. Se podría escribir algo como:

a + b = c; // This isn't right 

Tener el tipo de retorno del operador + como "SomeType const" permite el retorno por valor y al mismo tiempo impide la expresión de ser utilizado en el lado izquierdo de una asignación.

retorno como valor constante permite también evitar errores tipográficos como los siguientes:

if (someFunction() = 2) 

cuando la intención

if (someFunction() == 2) 

Si algunaFuncion() se declara como

const int someFunction() 

entonces el if() el tipado anterior sería capturado por el compilador.

• Retorno como referencia constante: Esta llamada a la función no puede aparecer en el lado izquierdo de una asignación, y desea evitar hacer una copia (devolver por valor). P.ej. supongamos que tenemos una clase Student y nos gustaría proporcionar un identificador de acceso() para obtener el ID del estudiante:

class Student 
{ 
    std::string id_; 

public: 

    const std::string& id() const; 
}; 

const std::string& Student::id() 
{ 
    return id_; 
} 

Considérese el descriptor de acceso id(). Esto debe declararse const para garantizar que la función de miembro id() no modificará el estado del objeto. Ahora, considere el tipo de devolución. Si el tipo de retorno eran cadena & entonces uno podría escribir algo como:

Student s; 
s.id() = "newId"; 

que no es lo que queremos.

Podríamos haber devuelto por valor, pero en este caso devolver por referencia es más eficiente. Hacer que el tipo de retorno sea una cadena const & evita adicionalmente que se modifique el id.

+0

Guau, la edición de rebajas realmente jodió aquí ... Yo no No sé cómo solucionarlo sin quitar los puntos. – aib

+0

Allí, reemplacé el formato de viñetas con los caracteres de viñetas reales (•). Creo que se ve mejor, pero puede retroceder. – aib

+0

Gracias a todos por el esfuerzo para responder la pregunta, pero esa respuesta acaba de llegar al punto. – user96815

0

Nunca pensé, que podemos devolver un valor const por referencia y no veo el valor de hacerlo .. Pero , tiene sentido si se intenta pasar un valor a una función como esta

void func(const int& a); 

esto tiene la ventaja de contar el compilador para no hacer una copia de la variable a en la memoria (que se realiza cuando se pasa un argumento de valor y no por referencia). El const está aquí para evitar que se modifique la variable a.

+0

Me preocupa el valor de retorno no el argumento. – user96815

+2

Bueno, para los tipos de tamaño de puntero, como int, la referencia constante no es significativa, ya que un valor (piénselo como un puntero para las referencias) aún debe insertarse en la pila al llamar a la función. Entonces "const int &" es esencialmente lo mismo que "int". La ganancia de pasar referencias de referencias a funciones realmente solo se ve con objetos "grandes". –

6

Lo básico para comprender es que devolver por valor creará una nueva copia de su objeto. La devolución por referencia devolverá una referencia a un objeto existente. NOTA: Al igual que los punteros, PUEDE tener referencias pendientes. Por lo tanto, no cree un objeto en una función y devuelva una referencia al objeto; se destruirá cuando la función regrese, y devolverá una referencia colgante.

retorno por valor:

  • Cuando se tiene POD (Llanura de datos antiguos)
  • Cuando se desea devolver una copia de un objeto

retorno por referencia:

  • Cuando usted tener una razón actuación para evitar una copia del objeto va a devolver, y que entiende el tiempo de vida del objeto
  • Cuando debe devolver una instancia particular de un objeto, y que entiende el tiempo de vida del objeto

Las referencias de Const/Constant lo ayudan a aplicar los contratos de su código y ayudan a los compiladores de sus usuarios a encontrar errores de uso. No afectan el rendimiento.

+1

+1, muy buena respuesta. Tenga en cuenta que el caso de retorno por valor no es tan caro como parece, porque el compilador puede eliminar la mayoría de las copias utilizando la Optimización del valor de retorno (y todos los compiladores no de juguete implementan esta optimización). –

1

Devolver un valor constante no es una expresión muy común, ya que de todos modos está devolviendo algo nuevo que solo la persona que llama puede tener, por lo que no es común tener un caso donde no puedan modificarlo. En su ejemplo, usted no sabe qué harán con él, entonces ¿por qué debería evitar que lo modifiquen?

Tenga en cuenta que en C++ si no dice que algo es una referencia o un puntero, es un valor por lo que creará una copia nueva en lugar de modificar el objeto original. Esto puede no ser totalmente obvio si proviene de otros idiomas que usan referencias por defecto.

Devolver una referencia o referencia constante significa que en realidad es otro objeto en otro lugar, por lo que cualquier modificación afectará a ese otro objeto. Un modismo común podría exponer a un miembro privado de una clase.

const significa que lo que sea no se puede modificar, por lo que si devuelve una referencia constante no puede llamar a ningún método no const ni modificar ningún miembro de datos.

+0

¿Es aconsejable implementar el método pop() de una pila que contiene una clase compleja para devolver la parte superior como referencia constante? ¿No podré modificar ningún atributo de esa clase? – user96815

+0

No. STL métodos pop (pop(), pop_back(), pop_front() etc) todos vuelven vacíos. Es casi seguro que quieres hacer lo mismo: si sacas el miembro superior de la pila, no puedes devolver una referencia porque ya no existe. Es posible que desee devolverlo por valor. Eso funcionaría, es solo una pequeña compensación entre la eficiencia (copia extra del artículo) y la conveniencia, si a menudo quiere abrir un elemento antes de usarlo. – Peter

+0

@Peter: hay otra solución de compromiso que no mencionó, que la Biblioteca Estándar de C++ reconoce al devolver el vacío en las funciones pop(), es decir, la fuerte garantía de excepción. –

1
  • Regresar por referencia en.

Puede devolver una referencia a algún valor, como un miembro de la clase. De esta forma, no creas copias. Sin embargo, no debe devolver las referencias a los valores en una pila, ya que da como resultado un comportamiento indefinido.

#include <iostream>                                   
using namespace std; 


class A{ 
private: int a; 

public: 
     A(int num):a(num){} 

     //a to the power of 4. 
     int& operate(){ 
       this->a*=this->a; 
       this->a*=this->a; 
       return this->a; 
     } 

     //return constant copy of a. 
     const int constA(){return this->a;} 

     //return copy of a. 
     int getA(){return this->a;} 
}; 

int main(){ 

     A obj(3); 
     cout <<"a "<<obj.getA()<<endl; 
     int& b=obj.operate(); //obj.operate() returns a reference! 

     cout<<"a^4 "<<obj.getA()<<endl; 
     b++; 
     cout<<"modified by b: "<<obj.getA()<<endl; 
     return 0; 
} 

b y obj.a "punto" en el mismo valor, por lo que la modificación b modifica el valor de obj.a.

$./a.out 
a 3 
a^4 81 
modified by b: 82 
  • devolver un valor const.

Por otro lado, devolver un valor const indica que dicho valor no se puede modificar. Hay que notar que el valor devuelto es una copia .: Por ejemplo,

constA()++; 

daría lugar a un error de compilación, ya que la copia devuelta por CONSTA() es constante. Pero esto es solo una copia, no implica que A :: a sea constante.

  • Devuelva una referencia de const.

Esto es similar a devolver un valor const, excepto que no se devuelve ninguna copia, sino una referencia al miembro real. Sin embargo, no puede ser modificado.

const int& refA(){return this->a;} 

const int& b = obj.refA(); 
b++; 

dará como resultado un error de compilación.

0

su primer caso:

const int exampleOne(); 

Con tipos simples como int, esto casi nunca es lo que quiere, porque el const no tiene sentido. Retorno por valor implica una copia, y se puede asignar a un objeto no const libremente:

int a = exampleOne(); // perfectly valid. 

Cuando veo esto, por lo general es porque el que escribió el código estaba tratando de ser const-corrección, que es loable, pero no entendía del todo las implicaciones de lo que estaban escribiendo. Sin embargo, hay casos con operadores sobrecargados y tipos personalizados donde puede hacer la diferencia.

Algunos compiladores (GCC más nuevos, Metrowerks, etc.) advierten sobre un comportamiento como este con tipos simples, por lo que debe evitarse.

+0

-1. En el primer ejemplo, la const es * no * inútil. Considere la sobrecarga del operador +. Si el tipo de devolución es "Tipo" y no es "const Type", entonces uno puede escribir código como "a + b = c" que no es correcto. – PlagueHammer

+0

+1, ya que el comentario anterior de Debajit es realmente incorrecto. (Es correcto decir que otros métodos non-const del operador sin asignación * pueden * ser llamados en un valor r no-const, pero su respuesta ya lo resuelve). –

1
const int exampleOne(); 

Devuelve una copia const de algunos int. Es decir, creas una nueva int que no se puede modificar. Esto no es realmente útil en la mayoría de los casos porque usted está creando una copia de todos modos, por lo que normalmente no le importa si se modifica. Entonces, ¿por qué no simplemente devuelve un int regular?

Puede hacer una diferencia para tipos más complejos, aunque su modificación puede tener efectos secundarios no deseados. (Conceptualmente, digamos que una función devuelve un objeto que representa un identificador de archivo. Si ese identificador es const, el archivo es de solo lectura, de lo contrario puede ser modificado. Entonces, en algunos casos, tiene sentido que una función devuelva un valor const. Pero, en general, la devolución de un valor constante es poco común.

int& exampleTwo(); 

Esto devuelve una referencia a un int.Esto no afecta a la vida útil de ese valor, sin embargo, por lo que esto puede conducir a un comportamiento indefinido en un caso como este:

int& exampleTwo() { 
    int x = 42; 
    return x; 
} 

estamos devolviendo una referencia a un valor que ya no existe. El compilador puede advertirte sobre esto, pero probablemente compilará de todos modos. Pero no tiene sentido y, tarde o temprano, provocará bloqueos funky. Sin embargo, esto se usa a menudo en otros casos. Si la función hubiera sido un miembro de la clase, podría devolver una referencia a una variable miembro, cuya duración duraría hasta que el objeto salga del alcance, lo que significa que el valor de retorno de la función sigue siendo válido cuando la función retorna.

Es mayormente el mismo que el anterior, devolviendo una referencia a algún valor sin tomar posesión de él ni afectar su duración. La principal diferencia es que ahora devuelve una referencia a un objeto const (inmutable). A diferencia del primer caso, esto es más útil, ya que ya no se trata de una copia que nadie más conoce, por lo que las modificaciones pueden ser visibles para otras partes del código. (Puede tener un objeto que no sea const donde esté definido, y una función que permita que otras partes del código accedan a él como const, devolviendo una referencia constante al mismo.

+1

En su primer ejemplo, la const * es * significativa y útil en muchos escenarios. Considere la sobrecarga del operador +. Si el tipo de devolución es "Tipo" y no es "const Type", entonces uno puede escribir código como "a + b = c" que no es correcto. – PlagueHammer

+1

No, no puedes. Si la función (u operador) devuelve un int (no una referencia), es un valor r, y no se puede asignar a. Si hubiera sido una referencia, estaría en lo cierto, pero operator + generalmente no debería devolver una referencia en primer lugar, por esta y otras razones. – jalf

+0

+1. Es extraño cómo las respuestas de calidad como esta no han llegado a la cima (todavía). Por cierto, creo que debe cambiar "devolver una clase a una variable miembro" para "devolver una referencia a una variable miembro" en el cuarto párrafo de texto corporal. –

0

Creo que su pregunta es en realidad dos preguntas:

  • ¿Cuáles son las implicaciones de devolver un const
  • ¿Cuáles son las implicaciones de devolver una referencia

Para darle una mejor respuesta, voy a explicar un poco más.. sobre ambos conceptos.

Con respecto a la palabra clave const

La palabra clave const significa que el objeto no puede ser modificado través de esa variable, por ejemplo:

MyObject *o1 = new MyObject; 
const MyObject *o2 = o1; 
o1->set(...); // Will work and will change the instance variables. 
o2->set(...); // Won't compile. 

Ahora, la palabra clave const se puede utilizar en tres diferente contextos:

  • Asegurando el persona que llama de un método que no modificará el objeto

Por ejemplo:

void func(const MyObject &o); 
void func(const MyObject *o); 

En ambos casos, cualquier modificación realizada en el objeto permanecerá fuera del ámbito de la función, es por eso que el uso de la palabra clave Const le aseguro a la persona que llama que no voy a modificar sus variables de instancia.

  • Asegurando al compilador que un método específico no mutar el objeto

Si usted tiene una clase y algunos métodos que "entiende" o "obtiene" la información de las variables de instancia sin modificarlos, a continuación, Debería poder usarlos incluso si se usa la palabra clave const.Por ejemplo:

class MyObject 
{ 
    ... 
public: 
    void setValue(int); 
    int getValue() const; // The const at the end is the key 
}; 

void funct(const MyObject &o) 
{ 
    int val = o.getValue(); // Will compile. 
    a.setValue(val); // Won't compile. 
} 
  • último, (su caso) devolver un valor const

Esto significa que el objeto devuelto no puede ser modificado o mutado directamente. Por ejemplo:

const MyObject func(); 

void func2() 
{ 
    int val = func()->getValue(); // Will compile. 
    func()->setValue(val); // Won't compile. 
    MyObject o1 = func(); // Won't compile. 
    MyObject o2 = const_cast<MyObject>(func()); // Will compile. 
} 

Más información sobre la palabra clave const: C++ Faq Lite - Const Correctness

En cuanto a las referencias

de vuelta o que reciben una referencia significa que el objeto no se duplicará. Esto significa que cualquier cambio realizado en el valor en sí se reflejará fuera del alcance de la función. Por ejemplo:

void swap(int &x, int &y) 
{ 
    int z = x; 
    x = y; 
    y = z; 
} 
int a = 2; b = 3; 
swap(a, b); // a IS THE SAME AS x inside the swap function 

Así, devolviendo un valor de referencia significa que el valor se puede cambiar, por ejemplo:

class Foo 
{ 
public: 
    ... 
    int &val() { return m_val; } 
private: 
    int m_val; 
}; 

Foo f; 
f.val() = 4; // Will change m_val. 

Más información acerca de referencias: C++ Faq Lite - Reference and value semantics

Ahora, respondiendo a sus preguntas

const int exampleOne(); 

Significa que el objeto devuelto no puede cambiar a través de la variable. Es más útil cuando devuelves objetos.

int& exampleTwo(); 

significa que el objeto devuelto es el mismo que el de dentro de la función y cualquier cambio realizado a ese objeto se reflejada dentro de la función.

const int& exampleThree(); 

significa que el objeto devuelto es el mismo que el de dentro de la función y no se puede modificar a través de esa variable.

+0

Buena visión general, pero omitió mencionar la diferencia más importante entre los tipos de devolución de valor y referencia: la devolución de una referencia a un objeto local o temporal se compilará pero causará bloqueos porque el valor se destruye antes de que llegue al código de llamada. Además, creo que quieres "func(). GetValue()" y "func(). SetValue()" en tu 4º fragmento de código. –

Cuestiones relacionadas