si necesita devolver un puntero, mejor use un valor de retorno
littleadv
6
¿Puedes explicar por qué? Este elogio no es muy útil. Mi pregunta es bastante legítima. Esto se está utilizando actualmente en código de producción. Simplemente no entiendo completamente por qué.
Matthew Hoggan
1
David lo resume bastante bien. El puntero en sí se está modificando.
chris
2
Paso de esta manera si voy a llamar al operador "Nuevo" en la función.
NDEthos
Respuestas:
142
Debería pasar un puntero por referencia si necesita modificar el puntero en lugar del objeto al que apunta el puntero.
Esto es similar a por qué se utilizan punteros dobles; usar una referencia a un puntero es un poco más seguro que usar punteros.
¿Tan similar a un puntero de un puntero, excepto un nivel menos de indirección para la semántica de paso por referencia?
Me gustaría agregar que a veces también desea devolver un puntero por referencia. Por ejemplo, si tiene una clase que contiene datos en una matriz asignada dinámicamente, pero desea proporcionar acceso (no constante) a estos datos al cliente. Al mismo tiempo, no desea que el cliente pueda manipular la memoria a través del puntero.
2
@William, ¿puedes dar más detalles sobre eso? Suena interesante. ¿Significa que el usuario de esa clase podría obtener un puntero al búfer de datos interno y leer / escribir en él, pero no puede, por ejemplo, liberar ese búfer usando deleteo volver a enlazar ese puntero a algún otro lugar de la memoria? ¿O me equivoqué?
BarbaraKwarc
2
@BarbaraKwarc Seguro. Supongamos que tiene una implementación simple de una matriz dinámica. Naturalmente, sobrecargará al []operador. Una posible firma (y de hecho natural) para esto sería const T &operator[](size_t index) const. Pero también podrías haberlo hecho T &operator[](size_t index). Podrías tener ambos al mismo tiempo. Este último te dejaría hacer cosas como myArray[jj] = 42. Al mismo tiempo, no está proporcionando un puntero a sus datos, por lo que la persona que llama no puede alterar la memoria (por ejemplo, eliminarla accidentalmente).
Oh. Pensé que estabas hablando de devolver una referencia a un puntero (tu redacción exacta: "devolver un puntero por referencia", porque eso era lo que preguntaba OP: pasar punteros por referencias, no solo referencias antiguas) como una forma elegante de dando acceso de lectura / escritura al búfer sin permitir manipular el búfer en sí (por ejemplo, liberarlo). Devolver una simple referencia antigua es algo diferente y lo sé.
BarbaraKwarc
65
Al 50% de los programadores de C ++ les gusta establecer sus punteros en nulos después de una eliminación:
template<typename T>void moronic_delete(T*& p){delete p;
p =nullptr;}
Sin la referencia, solo estaría cambiando una copia local del puntero, sin afectar a la persona que llama.
Cambiaré el sonido estúpido por descubrir la respuesta: ¿por qué se llama eliminación idiota? ¿Qué me estoy perdiendo?
Sammaron
1
@Sammaron Si miras el historial, la plantilla de la función solía ser llamada paranoid_delete, pero Puppy la renombró moronic_delete. Probablemente pertenezca al otro 50%;) De todos modos, la respuesta corta es que la configuración de punteros a null after deletecasi nunca es útil (porque deberían salir del alcance, de todos modos) y a menudo dificulta la detección de errores "use after free".
fredoverflow
@fredoverflow Entiendo tu respuesta, pero @Puppy parece pensar que deletees estúpido en general. Puppy, busqué en Google, no veo por qué eliminar es completamente inútil (tal vez yo también sea un buscador terrible de Google;)). ¿Puede explicar un poco más o proporcionar un enlace?
Sammaron
@Sammaron, C ++ tiene "punteros inteligentes" ahora que encapsulan punteros regulares y automáticamente llaman a "eliminar" cuando se salen de su alcance. Los punteros inteligentes son ampliamente preferidos a los punteros tontos de estilo C porque eliminan este problema por completo.
tjwrona1992
14
La respuesta de David es correcta, pero si aún es un poco abstracta, aquí hay dos ejemplos:
Es posible que desee poner a cero todos los punteros liberados para detectar problemas de memoria antes. Estilo C que harías:
Cuando se trata de listas vinculadas, a menudo se representan simplemente como punteros a un nodo siguiente:
structNode{value_t value;Node* next;};
En este caso, cuando inserta en la lista vacía, necesariamente debe cambiar el puntero entrante porque el resultado ya no es el NULLpuntero. Este es un caso en el que modifica un puntero externo de una función, por lo que tendría una referencia al puntero en su firma:
Tuve que usar un código como este para proporcionar funciones para asignar memoria a un puntero pasado y devolver su tamaño porque mi empresa me "objeta" usando el STL
int iSizeOfArray(int*&piArray){
piArray =newint[iNumberOfElements];...return iNumberOfElements;}
No es agradable, pero el puntero debe pasarse por referencia (o usar un puntero doble). De lo contrario, la memoria se asigna a una copia local del puntero si se pasa por valor, lo que da como resultado una pérdida de memoria.
Un ejemplo es cuando escribe una función de analizador y le pasa un puntero de origen para leer, si se supone que la función empuja ese puntero hacia adelante detrás del último carácter que ha sido reconocido correctamente por el analizador. El uso de una referencia a un puntero deja en claro que la función moverá el puntero original para actualizar su posición.
En general, usa referencias a punteros si desea pasar un puntero a una función y dejar que mueva ese puntero original a otra posición en lugar de simplemente mover una copia sin afectar el original.
¿Por qué el voto negativo? ¿Hay algún problema con lo que escribí? ¿Le importaria explicar? : P
BarbaraKwarc
0
Otra situación en la que puede necesitar esto es si tiene una colección stl de punteros y desea cambiarlos usando el algoritmo stl. Ejemplo de for_each en c ++ 98.
Respuestas:
Debería pasar un puntero por referencia si necesita modificar el puntero en lugar del objeto al que apunta el puntero.
Esto es similar a por qué se utilizan punteros dobles; usar una referencia a un puntero es un poco más seguro que usar punteros.
fuente
delete
o volver a enlazar ese puntero a algún otro lugar de la memoria? ¿O me equivoqué?[]
operador. Una posible firma (y de hecho natural) para esto seríaconst T &operator[](size_t index) const
. Pero también podrías haberlo hechoT &operator[](size_t index)
. Podrías tener ambos al mismo tiempo. Este último te dejaría hacer cosas comomyArray[jj] = 42
. Al mismo tiempo, no está proporcionando un puntero a sus datos, por lo que la persona que llama no puede alterar la memoria (por ejemplo, eliminarla accidentalmente).Al 50% de los programadores de C ++ les gusta establecer sus punteros en nulos después de una eliminación:
Sin la referencia, solo estaría cambiando una copia local del puntero, sin afectar a la persona que llama.
fuente
paranoid_delete
, pero Puppy la renombrómoronic_delete
. Probablemente pertenezca al otro 50%;) De todos modos, la respuesta corta es que la configuración de punteros a null afterdelete
casi nunca es útil (porque deberían salir del alcance, de todos modos) y a menudo dificulta la detección de errores "use after free".delete
es estúpido en general. Puppy, busqué en Google, no veo por qué eliminar es completamente inútil (tal vez yo también sea un buscador terrible de Google;)). ¿Puede explicar un poco más o proporcionar un enlace?La respuesta de David es correcta, pero si aún es un poco abstracta, aquí hay dos ejemplos:
Es posible que desee poner a cero todos los punteros liberados para detectar problemas de memoria antes. Estilo C que harías:
En C ++ para hacer lo mismo, puede hacer:
Cuando se trata de listas vinculadas, a menudo se representan simplemente como punteros a un nodo siguiente:
En este caso, cuando inserta en la lista vacía, necesariamente debe cambiar el puntero entrante porque el resultado ya no es el
NULL
puntero. Este es un caso en el que modifica un puntero externo de una función, por lo que tendría una referencia al puntero en su firma:Hay un ejemplo en esta pregunta .
fuente
Tuve que usar un código como este para proporcionar funciones para asignar memoria a un puntero pasado y devolver su tamaño porque mi empresa me "objeta" usando el STL
No es agradable, pero el puntero debe pasarse por referencia (o usar un puntero doble). De lo contrario, la memoria se asigna a una copia local del puntero si se pasa por valor, lo que da como resultado una pérdida de memoria.
fuente
Un ejemplo es cuando escribe una función de analizador y le pasa un puntero de origen para leer, si se supone que la función empuja ese puntero hacia adelante detrás del último carácter que ha sido reconocido correctamente por el analizador. El uso de una referencia a un puntero deja en claro que la función moverá el puntero original para actualizar su posición.
En general, usa referencias a punteros si desea pasar un puntero a una función y dejar que mueva ese puntero original a otra posición en lugar de simplemente mover una copia sin afectar el original.
fuente
Otra situación en la que puede necesitar esto es si tiene una colección stl de punteros y desea cambiarlos usando el algoritmo stl. Ejemplo de for_each en c ++ 98.
De lo contrario, si usa la firma changeObject (Object * item) , tiene una copia del puntero, no la original.
fuente