Esto es un poco subjetivo, creo; No estoy seguro de si la opinión será unánime (he visto muchos fragmentos de código donde se devuelven referencias).
De acuerdo con un comentario sobre esta pregunta que acabo de hacer, con respecto a la inicialización de referencias , devolver una referencia puede ser malo porque, [según tengo entendido], hace que sea más fácil no borrarlo, lo que puede provocar pérdidas de memoria.
Esto me preocupa, ya que he seguido ejemplos (a menos que esté imaginando cosas) y haya hecho esto en unos pocos lugares ... ¿He entendido mal? ¿Es malo? Si es así, ¿qué tan malvado?
Siento que debido a mi mezcla de punteros y referencias, combinado con el hecho de que soy nuevo en C ++, y la confusión total sobre qué usar cuando, mis aplicaciones deben ser una pérdida de memoria ...
Además, entiendo que el uso de punteros inteligentes / compartidos es generalmente aceptado como la mejor manera de evitar pérdidas de memoria.
Respuestas:
En general, devolver una referencia es perfectamente normal y ocurre todo el tiempo.
Si te refieres a:
Eso es todo tipo de maldad. La pila asignada
i
desaparecerá y no te estás refiriendo a nada. Esto también es malo:Porque ahora el cliente finalmente tiene que hacer lo extraño:
Tenga en cuenta que las referencias de valor siguen siendo solo referencias, por lo que todas las aplicaciones malvadas siguen siendo las mismas.
Si desea asignar algo que vive más allá del alcance de la función, use un puntero inteligente (o, en general, un contenedor):
Y ahora el cliente almacena un puntero inteligente:
Las referencias también están bien para acceder a cosas donde sabe que la vida útil se mantiene abierta en un nivel superior, por ejemplo:
Aquí sabemos que está bien devolver una referencia
i_
porque lo que sea que nos llame gestiona la vida útil de la instancia de clase, pori_
lo que vivirá al menos ese tiempo.Y, por supuesto, no hay nada malo con solo:
Si el tiempo de vida debe dejarse en manos de la persona que llama, y solo está calculando el valor.
Resumen: está bien devolver una referencia si la vida útil del objeto no terminará después de la llamada.
fuente
return new int
.No. No, no, mil veces no.
Lo que es malo es hacer referencia a un objeto asignado dinámicamente y perder el puntero original. Cuando usted
new
objeta, asume la obligación de tener una garantíadelete
.Pero eche un vistazo, por ejemplo,
operator<<
que debe devolver una referencia, ono funciona
fuente
Debe devolver una referencia a un objeto existente que no desaparecerá de inmediato y donde no tiene intención de transferir ninguna propiedad.
Nunca devuelva una referencia a una variable local o algo así, porque no estará allí para ser referenciada.
Puede devolver una referencia a algo independiente de la función, que no espera que la función de llamada asuma la responsabilidad de eliminar. Este es el caso de la
operator[]
función típica .Si está creando algo, debe devolver un valor o un puntero (normal o inteligente). Puede devolver un valor libremente, ya que entra en una variable o expresión en la función de llamada. Nunca devuelva un puntero a una variable local, ya que desaparecerá.
fuente
Las respuestas no me parecen satisfactorias, así que agregaré mis dos centavos.
Analicemos los siguientes casos:
Uso erróneo
Esto obviamente es un error
Uso con variables estáticas
Esto es correcto, porque las variables estáticas existen durante toda la vida de un programa.
Esto también es bastante común cuando se implementa el patrón Singleton
Uso:
Operadores
Los contenedores de biblioteca estándar dependen en gran medida del uso de operadores que devuelven referencias, por ejemplo
puede usarse en lo siguiente
Acceso rápido a datos internos.
Hay momentos en los que & se puede usar para acceder rápidamente a datos internos
con uso:
SIN EMBARGO, esto puede conducir a dificultades como esta:
fuente
true
:If((a*b) == (c*d))
Container::data()
La implementación de debería leerreturn m_data;
No es malo Al igual que muchas cosas en C ++, es bueno si se usa correctamente, pero hay muchas dificultades que debe tener en cuenta al usarlo (como devolver una referencia a una variable local).
Hay cosas buenas que se pueden lograr con él (como map [name] = "hello world")
fuente
map[name] = "hello world"
?HashMap<String,Integer>
en Java? : PNo es verdad. Devolver una referencia no implica semántica de propiedad. Es decir, solo porque haces esto:
... no significa que ahora posee la memoria a la que se refiere v;
Sin embargo, este es un código horrible:
Si está haciendo algo como esto porque "no necesita un puntero en esa instancia", entonces: 1) simplemente desreferenciar el puntero si necesita una referencia, y 2) eventualmente necesitará el puntero, porque debe hacer coincidir un nuevo con una eliminación, y necesita un puntero para llamar a la eliminación.
fuente
Hay dos casos:
referencia constante: buena idea, a veces, especialmente para objetos pesados o clases proxy, optimización del compilador
referencia no constante: una mala idea, a veces, rompe encapsulaciones
Ambos comparten el mismo problema: pueden señalar potencialmente objetos destruidos ...
Recomendaría usar punteros inteligentes para muchas situaciones en las que necesita devolver una referencia / puntero.
Además, tenga en cuenta lo siguiente:
Existe una regla formal: el estándar C ++ (sección 13.3.3.1.4 si está interesado) establece que un temporal solo puede vincularse a una referencia constante; si intenta utilizar una referencia no constante, el compilador debe marcar esto como un error.
fuente
No solo no es malo, a veces es esencial. Por ejemplo, sería imposible implementar el operador [] de std :: vector sin usar un valor de retorno de referencia.
fuente
operator[]
para un contenedor sin usar una referencia ... y lostd::vector<bool>
hace. (Y crea un verdadero desastre en el proceso)::std::vector<bool>
ha mencionado).std::vector<T>
código de plantilla está roto, si esT
posiblebool
, porquestd::vector<bool>
tiene un comportamiento muy diferente al de otras instancias. Es útil, pero debería haber recibido su propio nombre y no una especializaciónstd::vector
.Adición a la respuesta aceptada:
Yo diría que este ejemplo no está bien y debería evitarse si es posible. ¿Por qué? Es muy fácil terminar con una referencia colgante .
Para ilustrar el punto con un ejemplo:
entrando en la zona de peligro:
fuente
la referencia de retorno se usa generalmente en la sobrecarga de operadores en C ++ para objetos grandes, porque devolver un valor necesita una operación de copia (en la sobrecarga del perador, generalmente no usamos el puntero como valor de retorno)
Pero la referencia de retorno puede causar problemas de asignación de memoria. Debido a que una referencia al resultado se pasará de la función como una referencia al valor de retorno, el valor de retorno no puede ser una variable automática.
si desea usar referencias de retorno, puede usar un búfer de objeto estático. por ejemplo
de esta manera, puede usar la referencia de retorno de forma segura.
Pero siempre puede usar el puntero en lugar de la referencia para devolver el valor en functiong.
fuente
Creo que usar la referencia como valor de retorno de la función es mucho más sencillo que usar el puntero como valor de retorno de la función. En segundo lugar, siempre sería seguro utilizar una variable estática a la que se refiera el valor de retorno.
fuente
Lo mejor es crear un objeto y pasarlo como parámetro de referencia / puntero a una función que asigna esta variable.
Asignar un objeto en la función y devolverlo como referencia o puntero (sin embargo, el puntero es más seguro) es una mala idea debido a que libera memoria al final del bloque de funciones.
fuente
La función getPtr puede acceder a la memoria dinámica después de la eliminación o incluso a un objeto nulo. Lo que puede causar malas excepciones de acceso. En cambio, getter y setter deben implementarse y verificarse el tamaño antes de regresar.
fuente
La función como lvalue (también conocido como devolución de referencias no constantes) debe eliminarse de C ++. Es terriblemente poco intuitivo. Scott Meyers quería un min () con este comportamiento.
lo cual no es realmente una mejora en
Este último incluso tiene más sentido.
Me doy cuenta de que la función como lvalue es importante para las secuencias de estilo C ++, pero vale la pena señalar que las secuencias de estilo C ++ son terribles. No soy el único que piensa esto ... ya que recuerdo que Alexandrescu tenía un gran artículo sobre cómo hacerlo mejor, y creo que boost también ha tratado de crear un mejor método de E / S seguro.
fuente
vector::operator[]
por ejemplo. ¿Prefieres escribirv.setAt(i, x)
ov[i] = x
? Este último es MUY superior.v.setAt(i, x)
cualquier momento. Es muy superior.Me encontré con un problema real donde realmente era malvado. Esencialmente, un desarrollador devolvió una referencia a un objeto en un vector. ¡¡¡Eso fue malo!!!
Los detalles completos sobre los que escribí en enero: http://developer-resource.blogspot.com/2009/01/pros-and-cons-of-returing-references.html
fuente
Sobre código horrible:
Entonces, de hecho, el puntero de memoria se perdió después del regreso. Pero si usa shared_ptr así:
La memoria no se pierde después del regreso y se liberará después de la asignación.
fuente