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?
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?
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.
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.
std::vector
tiene operator[]
que no permitiría vec[n] = m
en caso contrario.
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);
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
.
Y esta implementación es difícil de refactorizar cuando necesita eliminar o reemplazar algún_param_ con un objeto de otro tipo. – Basilevs
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.
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
No sé por qué, pero odio las clases matriciales (y soy un analista numérico) –
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
El código que ha publicado no es legal. C++: las referencias deben inicializarse. –
Otro caso clásico:
class Foo {
Foo();
public:
static Foo& getSingleton();
};
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);
}
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&
.
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 –
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: | –
Incluso puede devolver por valor y asignarlo a un temporal. Es por eso que Scott Meyers recomienda regresar por valor de const. – Philipp