c++ - example - ¿Deben los accesores devolver valores o referencias constantes?
map find c++ reference (8)
AFAIK, la regla es la misma que se usa al decidir si se toma un parámetro de función por valor o por referencia constante. Si el sizeof
del valor que se está devolviendo es lo suficientemente pequeño, entonces tiendo a usar devolver una copia o devolver una referencia constante.
Supongamos que tengo una clase Foo
con un miembro std::string
str
. ¿Qué debería obtener get_str
volver?
std::string Foo::get_str() const
{
return str;
}
o
const std::string& Foo::get_str() const
{
return str;
}
¿Qué es más idiomático en C ++?
Creo que la segunda implementación (referencia constante) es correcta como:
- El objeto devuelto es inmutable y, por lo tanto, mantiene las reglas de encapsulación.
- Es un poco más eficiente ya que no hay copia de
str
.
Sin embargo, el primer enfoque funcionará casi igual de bien.
Depende de lo que quieras hacer con el valor de retorno.
Esto es mejor si solo desea hacer una consulta y no modificar la str
.
const std::string& Foo::get_str() const
{
return str;
}
De lo contrario, ve por esto:
std::string& Foo::get_str()
{
return str;
}
Y si quieres una copia / clon de str
, entonces usa esto:
std::string Foo::get_str() const
{
return str;
}
Devolver por valor significa que no tiene que tener un std :: string interno almacenado en algún lugar de la clase para la cual regresa.
En un método virtual puro, es preferible no suponer que std :: string estará allí y, por lo tanto, devolver un std :: string por valor.
En una clase concreta donde claramente hay un miembro std :: string y solo va a devolverle una referencia, puede, por eficiencia, devolverlo por referencia constante. Incluso si tiene que cambiarlo más adelante, no necesita cambiar la funcionalidad que usa la clase.
En un modelo de subprocesos múltiples en el que la cadena interna podría cambiar entre llamadas, por supuesto, es probable que tenga que regresar por valor (suponiendo que los usuarios de la clase obtendrán una vista "instantánea" del valor de la cadena en el momento de la finalización de la llamada).
La devolución por referencia suele ser más eficiente. Sin embargo, tengo una clase de cadena contabilizada de referencia que no se puede cambiar, que puede devolver por valor de manera eficiente y solía usarla con bastante frecuencia.
Por cierto, algunos recomendarían devolver un valor std :: string by const. No creo que sea la mejor manera de hacerlo, ya que evita que el usuario pueda "intercambiarlo" con una variable local.
En general (a menos que haya un problema de rendimiento comprobado), devolvería por valor.
En primer lugar, hay una diferencia semántica, si los cambios en su propiedad, ¿desea que sus clientes se actualicen del cambio o obtengan el valor en el momento de llamar a la función?
Existe un problema obvio de corrección: si devuelve por referencia la entidad que llama a la función puede aferrarse a la referencia y puede usarla después de que su objeto haya sido destruido (lo que no es tan bueno).
Otro problema es con el código de varios subprocesos, si un subproceso se lee en la referencia constante mientras está actualizando la variable en su lugar por muchos problemas.
En cualquier caso, creo que el caso de uso más común es cuando el llamador de la función almacena el valor en una variable.
string val = obj->get_str();
// use val now
Si esto es cierto (a diferencia de cout << obj->get_str()
donde no hay una variable), siempre tiene que construir una nueva cadena para val
incluso si regresa por referencia y ya que los compiladores pueden realizar RVO la versión por valor no realizará un rendimiento inferior a la variante by-const-ref.
En conclusión: si sabe que se trata de un problema de rendimiento y está seguro de que el valor de retorno no se almacenará durante más tiempo del que existirá su objeto y que no espera que se use desde diferentes subprocesos, entonces está bien devolverlo por referencia constante .
En general, debe devolver los POD por valor (por ejemplo, int, short, char, long, etc.) y una referencia constante para tipos más complejos:
int getData() const;
short getMoreData() const;
const std::string& getName() const;
const ComplexObject& getComplexData() const;
La respuesta corta es: depende :-)
Desde el punto de vista del rendimiento, devolver una referencia es (generalmente) mejor: guarda la creación de un nuevo objeto std::string
. (En este caso, la creación es lo suficientemente costosa y el tamaño del objeto es lo suficientemente alto como para justificar que esta elección valga la pena, pero no siempre es así. Con un tipo más pequeño o integrado, la diferencia de rendimiento puede ser insignificante, o devolver por valor incluso puede ser más barato).
Desde el punto de vista de la seguridad, devolver una copia del valor original puede ser mejor, ya que la constancia puede ser eliminada por clientes malintencionados. Esto se debe tener especialmente en cuenta si el método es parte de una API pública, es decir, usted (equipo r) no tiene control total sobre cómo se utiliza (mal) el valor devuelto.
Uno de los objetivos de tener un método de acceso es intentar, al menos en cierta medida, abstraer la implementación de su clase de su interfaz.
Devolver por valor es mejor porque no hay problemas de por vida con el objeto al que se hace referencia. Si decide no tener un miembro std::string
pero, digamos, un std::stringstream
o para crear un std::string
sobre la marcha, no tiene que cambiar la interfaz.
Devolver por referencia const
no es lo opuesto a tomar un parámetro por referencia constante, tomar un valor por referencia const
no vincula su representación interna de datos a la interfaz externa.