2010-07-09 13 views
16

En C++,¿Cuál es la importancia del retorno por referencia?

function() = 10; 

funciona si la función devuelve una variable por referencia.

¿Cuáles son los casos de uso de la misma?

+3

Sin embargo, no es necesario que funcione esa sintaxis. Siempre puedes devolver un objeto de la clase proxy que tiene su 'operator =' hacer la asignación detrás de las escenas –

+1

es por eso que nunca entendí C++ ... nunca dejas de encontrar nuevas piezas de sintaxis que te pasen por alto por completo ... .me encantaría sobrecargar al operador: | –

+0

Incluso puede devolver por valor y asignarlo a un temporal. Es por eso que Scott Meyers recomienda regresar por valor de const. – Philipp

Respuesta

20

El caso más común es implementar cosas como operador [].

struct A { 
    int data[10]; 
    int & operator[](int i) { 
     return data[i]; 
    } 
}; 

Otra es la de devolver un objeto grande de una clase a través de una función de accesor:

struct b { 
    SomeBigThing big; 
    const SomeBigThing & MyBig() const { 
     return big; 
    } 
}; 

con el fin de evitar la sobrecarga de copia.

2

en caso de tener una clase que contiene otra estructura, que puede ser útil para modificar directamente la estructura contenida:

struct S 
{ 
    int value; 
}; 

class C 
{ 
    public: 

     S& ref() { return m_s; } 

    private: 

     S m_s; 
}; 

le permite escribir algo como:

void foo() 
{ 
    C c; 

    // Now you can do that: 

    c.ref().value = 1; 
} 

Nota: en este ejemplo, podría ser más sencillo hacer público m_s directamente en lugar de devolver una referencia.

0

std::vector tiene operator[] que no permitiría vec[n] = m en caso contrario.

3

Un caso de uso muy normal es cuando se escribe una matriz como clase. Aquí desea sobrecargar el operator [] así como se puede hacer a[0] = 10; En ese caso, le gustaría que la firma sea como int& operator[](int index);

4

getters/setters por ejemplo

class C 
{ 
    int some_param_; 
public: 
    int& param() { return some_param_; } 
    int const& param() const { return some_param_; } 
}; 

pero aquí hay que ir con some_param ser una público int. Los contenedores proporcionan funciones que regresan por referencia, por ej. vector<T>::operator[] para que pueda escribir v[k] = x.

+0

Y esta implementación es difícil de refactorizar cuando necesita eliminar o reemplazar algún_param_ con un objeto de otro tipo. – Basilevs

2

atornilla SO hasta mi respuesta

Ni siquiera necesita devolver una referencia:

struct C { }; 

C f() { 
    return C(); 
} 

int main() { 
    C a; 
    f() = a; // compiles fine 
} 

Debido a este comportamiento es bastante sorprendente, se debe normalmente devuelven un valor const o una const referencia a menos que el usuario tenga una intención sensata de modificar el resultado.

2

Puede ser muy útil en la aplicación de métodos de acceso

class Matrix 
{ 
    public: 
     //I skip constructor, destructor etc 

     int & operator()(int row, int col) 
     { 
     return m_arr[row + col * size]; 
     } 

    private: 
     int size; 
     int * m_arr; 
} 

Matrix m(10); 
m(1,0) = 10; //assign a value to row 1, col 0 
+0

No sé por qué, pero odio las clases matriciales (y soy un analista numérico) –

7

Consideremos el siguiente código, MyFunction devuelve un puntero a un int, y se establece un valor a la int.

int *i; 
i = MyFunction(); 
*i = 10; 

Ahora que acortar a

*(MyFunction()) = 10; 

que hace exactamente lo mismo que el primer bloque de código.

Puede ver una referencia como solo un puntero que siempre se desreferencia. Así que si mi función devuelve una referencia - no un puntero - a un int del bloque de código Frist se convertiría en

int &i; 
i = MyFunction(); 
i = 10; 

y el segundo se convertiría en

MyFunction() = 10; 

Esto es lo que estaba buscando

+2

El código que ha publicado no es legal. C++: las referencias deben inicializarse. –

1

Otro caso clásico:

class Foo { 
    Foo(); 
public: 
    static Foo& getSingleton(); 
}; 
0

también puede lograr método de encadenamiento (si así lo desea) u cantar retorno por referencia.

class A 
{ 
public: 
    A& method1() 
    { 
     //do something 
     return *this; //return ref to the current object 
    } 
    A& method2(int i); 
    A& method3(float f); //other bodies omitted for brevity 
}; 

int main() 
{ 
    A aObj; 
    aObj.method1().method2(5).method3(0.75); 

    //or use it like this, if you prefer 
    aObj.method1() 
     .method2(5) 
     .method3(0.75); 
} 
0

El named parameter idiom es otro caso de uso. Consideran

class Foo 
{ 
public: 
    Foo(
     int lions, 
     float tigers, 
     double bears, 
     std::string zookeeper 
    ); 
}; 

usuarios de esta clase deben recordar la posición de cada parámetro

Foo foo(1, 2.0, 5, "Fred"); 

que puede no ser evidente sin mirar a la cabecera. En comparación con una clase creador como tal

class CreateFoo 
{ 
friend class Foo; 
public: 
    CreateFoo(); 

    CreateFoo& lions(int lions) { 
     _lions = lions; 
     return *this; 
    } 

    CreateFoo& tigers(float tigers) { 
     _tigers = tigers; 
     return *this; 
    } 

    CreateFoo& bears(double bears) { 
     _bears = bears; 
     return *this; 
    } 

    CreateFoo& zookeeper(const std::string& zookeeper) { 
     _zookeeper = zookeeper; 
     return *this; 
    } 

private: 
    int _lions; 
    float _tigers; 
    double _bears; 
    std::string _zookeeper; 
}; 

que luego puede ser utilizado por los clientes al igual que

Foo foo = CreateFoo(). 
    lions(1). 
    tigers(2.0). 
    zookeeper("Fred"). 
    bears(5) 
    ; 

asumiendo Foo tiene un constructor que toma un const CreateFoo&.

Cuestiones relacionadas