valores tipos salida retornan referencias referencia que punteros por paso parametros parametro operadores funciones entrada con c++ const

c++ - tipos - Devolver una referencia constante a un objeto en lugar de una copia



que es un parametro en c++ (12)

¿Importa? Tan pronto como utilice un compilador de optimización moderno, las funciones que regresen por valor no implicarán una copia a menos que se requiera semánticamente.

Vea las preguntas frecuentes de C ++ lite sobre esto.

Mientras refactorizaba algunos códigos, encontré algunos métodos getter que devuelven std :: string. Algo como esto, por ejemplo:

class foo { private: std::string name_; public: std::string name() { return name_; } };

Seguramente el getter sería mejor devolver un const std::string& ? El método actual es devolver una copia que no es tan eficiente. ¿Devolvería una referencia const en su lugar causaría algún problema?


Algunas implementaciones de std :: string comparten memoria con semántica de copiado en escritura, por lo que el retorno por valor puede ser casi tan eficiente como el retorno por referencia y no tiene que preocuparse por los problemas de duración (el tiempo de ejecución sí lo hace para ti).

Si te preocupa el rendimiento, haz una evaluación comparativa (<= no puedo enfatizarlo lo suficiente) !!! Pruebe ambos enfoques y mida la ganancia (o la falta de ella). Si uno es mejor y realmente te importa, entonces úsalo. Si no, entonces prefiera el valor por valor de la protección que ofrece contra problemas de por vida mencionados por otras personas.

Ya sabes lo que dicen sobre hacer suposiciones ...


De acuerdo, entonces las diferencias entre devolver una copia y devolver la referencia son:

  • Rendimiento : la devolución de la referencia puede ser o no más rápida; depende de cómo std::string se implementa mediante la implementación del compilador (como han señalado otros). Pero incluso si devuelve la referencia, la asignación después de la llamada a la función generalmente implica una copia, como en std::string name = obj.name();

  • Seguridad : devolver la referencia puede o no causar problemas (referencia colgante). Si los usuarios de su función no saben lo que están haciendo, almacenar la referencia como referencia y usarla después de que el objeto que proporciona el suministro quede fuera del alcance, entonces hay un problema.

Si lo quieres de forma rápida y segura usa boost :: shared_ptr . Su objeto puede almacenar internamente la cadena como shared_ptr y devolver un shared_ptr . De esta forma, no se copiará el objeto en marcha y siempre es seguro (a menos que los usuarios extraigan el puntero sin procesar con get() y hagan cosas con él después de que su objeto salga del alcance).


Depende de lo que necesites hacer. Tal vez quiera que todos los que llaman modifiquen el valor devuelto sin cambiar la clase. Si devuelve la referencia constante que no volará.

Por supuesto, el siguiente argumento es que la persona que llama podría hacer su propia copia. Pero si sabe cómo se usará la función y sabe que sucede de todos modos, entonces quizás hacerlo le ahorrará un paso más adelante en el código.


Devolver una referencia a un miembro expone la implementación de la clase. Eso podría evitar cambiar la clase. Puede ser útil para métodos privados o protegidos en caso de que se necesite la optimización. ¿Qué debería devolver un getter de C ++?


En realidad, otro problema específico al devolver una cadena no por referencia es el hecho de que std::string proporciona acceso mediante un puntero a un const char* interno a través del método c_str() . Esto me ha causado muchas horas de dolor de cabeza de depuración. Por ejemplo, digamos que quiero obtener el nombre de foo, y pasarlo a JNI para ser usado para construir una jstring para pasar a Java más adelante, y ese name() está devolviendo una copia y no una referencia. Podría escribir algo como esto:

foo myFoo = getFoo(); // Get the foo from somewhere. const char* fooCName = foo.name().c_str(); // Woops! foo.name() creates a temporary that''s destructed as soon as this line executes! jniEnv->NewStringUTF(fooCName); // No good, fooCName was released when the temporary was deleted.

Si su interlocutor va a hacer este tipo de cosas, podría ser mejor utilizar algún tipo de puntero inteligente, o una referencia constante, o al menos tener un desagradable encabezado de comentario de advertencia sobre su método foo.name (). Menciono JNI porque los antiguos programadores de Java podrían ser particularmente vulnerables a este tipo de encadenamiento de métodos que, de lo contrario, puede parecer inofensivo.


Es concebible que pueda romper algo si la persona que llama realmente desea una copia, porque estaban a punto de alterar el original y deseaban conservar una copia del mismo. Sin embargo, es mucho más probable que, de hecho, solo devuelva una referencia constante.

Lo más fácil es probarlo y luego probarlo para ver si todavía funciona, siempre que tenga algún tipo de prueba que pueda ejecutar. De lo contrario, me centraría en escribir primero la prueba, antes de continuar con la refactorización.


La única forma en que esto puede causar un problema es si la persona que llama almacena la referencia, en lugar de copiar la cadena, e intenta usarla después de que se destruye el objeto. Me gusta esto:

foo *pFoo = new foo; const std::string &myName = pFoo->getName(); delete pFoo; cout << myName; // error! dangling reference

Sin embargo, dado que su función existente devuelve una copia, entonces no rompería ninguno de los códigos existentes.


Las probabilidades son bastante buenas de que el uso típico de esa función no se rompa si cambia a una referencia constante.

Si todo el código que llama a esa función está bajo su control, solo haga el cambio y vea si el compilador se queja.


Lo cambiaría para devolver const std :: string &. La persona que llama probablemente realice una copia del resultado de todos modos si no cambia todo el código de llamada, pero no presentará ningún problema.

Una posible arruga surge si tiene múltiples hilos llamando al nombre (). Si devuelve una referencia, pero luego cambia el valor subyacente, el valor de la persona que llama cambiará. Pero el código existente no parece seguro para subprocesos de todos modos.

Eche un vistazo a la respuesta de Dima para un problema relacionado potencial pero poco probable.


Normalmente regreso const ya menos que no pueda. QBziZ da un ejemplo de dónde este es el caso. Por supuesto, QBziZ también afirma que std :: string tiene una semántica de copiar y escribir, que rara vez se cumple hoy en día, ya que COW implica una gran sobrecarga en un entorno de subprocesos múltiples. Al devolver const y le pones la responsabilidad a la persona que llama para hacer lo correcto con la cadena en su extremo. Pero dado que se trata de un código que ya está en uso, probablemente no debería cambiarlo a menos que el perfil demuestre que la copia de esta cadena está causando problemas de rendimiento masivo. Luego, si decides cambiarlo, deberás probar a fondo para asegurarte de que no hayas roto nada. Esperemos que los otros desarrolladores con los que trabajas no hagan cosas incompletas como en la respuesta de Dima.


Un problema para la devolución de referencia const sería si el usuario codifica algo así como:

const std::string & str = myObject.getSomeString() ;

Con un std::string return, el objeto temporal permanecería vivo y se adjuntaría a str hasta que str fuera del alcance.

Pero, ¿qué ocurre con una const std::string & ? Mi suposición es que tendríamos una referencia constante a un objeto que podría morir cuando su objeto padre lo desasigna:

MyObject * myObject = new MyObject("My String") ; const std::string & str = myObject->getSomeString() ; delete myObject ; // Use str... which references a destroyed object.

Así que mi preferencia va al retorno de referencia constante (porque, de todos modos, estoy más cómodo enviando una referencia que esperando que el compilador optimice el extra temporal), siempre que se respete el siguiente contrato: "si lo quieres más allá" la existencia de mi objeto, lo copian antes de la destrucción de mi objeto "