¿En qué circunstancias le gustaría utilizar código de esta naturaleza en c ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
¿En qué circunstancias le gustaría utilizar código de esta naturaleza en c ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
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.
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í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).
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.
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 delete
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:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
En C ++ para hacer lo mismo, puede hacer:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
Cuando se trata de listas vinculadas, a menudo se representan simplemente como punteros a un nodo siguiente:
struct Node
{
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 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:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
Hay un ejemplo en esta pregunta .
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 = new int[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.
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.
struct Storage {
typedef std::list<Object*> ObjectList;
ObjectList objects;
void change() {
typedef void (*ChangeFunctionType)(Object*&);
std::for_each<ObjectList::iterator, ChangeFunctionType>
(objects.begin(), objects.end(), &Storage::changeObject);
}
static void changeObject(Object*& item) {
delete item;
item = 0;
if (someCondition) item = new Object();
}
};
De lo contrario, si usa la firma changeObject (Object * item) , tiene una copia del puntero, no la original.