Sé que el título suena familiar ya que hay muchas preguntas similares, pero estoy pidiendo un aspecto diferente del problema (sé la diferencia entre tener cosas en la pila y ponerlas en el montón).
En Java siempre puedo devolver referencias a objetos "locales"
public Thing calculateThing() {
Thing thing = new Thing();
// do calculations and modify thing
return thing;
}
En C ++, para hacer algo similar tengo 2 opciones
(1) Puedo usar referencias siempre que necesite "devolver" un objeto
void calculateThing(Thing& thing) {
// do calculations and modify thing
}
Entonces úsalo así
Thing thing;
calculateThing(thing);
(2) O puedo devolver un puntero a un objeto asignado dinámicamente
Thing* calculateThing() {
Thing* thing(new Thing());
// do calculations and modify thing
return thing;
}
Entonces úsalo así
Thing* thing = calculateThing();
delete thing;
Usando el primer enfoque, no tendré que liberar memoria manualmente, pero para mí hace que el código sea difícil de leer. El problema con el segundo enfoque es que tendré que recordarlo delete thing;
, lo que no se ve muy bien. No quiero devolver un valor copiado porque es ineficiente (creo), así que aquí vienen las preguntas
- ¿Hay una tercera solución (que no requiera copiar el valor)?
- ¿Hay algún problema si me quedo con la primera solución?
- ¿Cuándo y por qué debería usar la segunda solución?
fuente
Respuestas:
Pruébalo.
Busque RVO y NRVO, y en C ++ 0x move-semántica. En la mayoría de los casos en C ++ 03, un parámetro de salida es solo una buena manera de hacer que su código sea feo, y en C ++ 0x realmente se estaría lastimando al usar un parámetro de salida.
Simplemente escriba código limpio, devuelva por valor. Si el rendimiento es un problema, perfílelo (deje de adivinar) y encuentre lo que puede hacer para solucionarlo. Es probable que no devuelva cosas de las funciones.
Dicho esto, si estás empeñado en escribir así, probablemente quieras hacer el parámetro out. Evita la asignación dinámica de memoria, que es más segura y generalmente más rápida. Requiere que tenga alguna forma de construir el objeto antes de llamar a la función, lo que no siempre tiene sentido para todos los objetos.
Si desea utilizar la asignación dinámica, lo menos que se puede hacer es ponerla en un puntero inteligente. (De todos modos, esto debe hacerse todo el tiempo). Entonces no te preocupes por eliminar nada, las cosas son seguras, etc. ¡El único problema es que es más lento que regresar por valor de todos modos!
fuente
Simplemente cree el objeto y devuélvalo
Creo que te harás un favor si te olvidas de la optimización y solo escribes código legible (necesitarás ejecutar un generador de perfiles más adelante, pero no optimices previamente).
fuente
Thing thing();
declara una función local y devuelve aThing
.Solo devuelve un objeto como este:
Esto invocará el constructor de copia en Things, por lo que es posible que desee hacer su propia implementación de eso. Me gusta esto:
Esto podría funcionar un poco más lento, pero podría no ser un problema en absoluto.
Actualizar
El compilador probablemente optimizará la llamada al constructor de la copia, por lo que no habrá sobrecarga adicional. (Como dreamlax señaló en el comentario).
fuente
Thing thing();
declara una función local que devuelve unThing
, también, el estándar permite que el compilador omita el constructor de copia en el caso que usted presentó; cualquier compilador moderno probablemente lo hará.¿Intentó utilizar punteros inteligentes (si Thing es un objeto realmente grande y pesado), como auto_ptr:
fuente
auto_ptr
s están en desuso; usarshared_ptr
o en suunique_ptr
lugar.Una forma rápida de determinar si se llama a un constructor de copias es agregar el registro al constructor de copias de su clase:
Llamada
someFunction
; el número de líneas "Copiar constructor se llamó" que obtendrá variará entre 0, 1 y 2. Si no obtiene ninguna, entonces su compilador ha optimizado el valor de retorno (lo que está permitido). Si obtiene no reciben 0, y el constructor de copia es ridículamente caro, a continuación, buscar formas alternativas para devolver las instancias de sus funciones.fuente
En primer lugar, tiene un error en el código, quiere decir que lo tiene
Thing *thing(new Thing());
, y soloreturn thing;
.shared_ptr<Thing>
. Lo dejó como si fuera un puntero. Se eliminará cuando la última referencia aThing
contenido quede fuera de alcance.fuente
Estoy seguro de que un experto en C ++ vendrá con una mejor respuesta, pero personalmente me gusta el segundo enfoque. El uso de punteros inteligentes ayuda con el problema de olvidar
delete
y, como usted dice, parece más limpio que tener que crear un objeto de antemano (y aún tener que eliminarlo si desea asignarlo en el montón).fuente