¿Para qué sirve la dirección indirecta múltiple en C++?
Pregunta
¿En qué circunstancias podría querer utilizar dirección indirecta múltiple (es decir, una cadena de punteros como en Foo **
) en C++?
Solución
El uso más común, como señaló @aku, es permitir que un cambio en un parámetro de puntero sea visible después de que regrese la función.
#include <iostream>
using namespace std;
struct Foo {
int a;
};
void CreateFoo(Foo** p) {
*p = new Foo();
(*p)->a = 12;
}
int main(int argc, char* argv[])
{
Foo* p = NULL;
CreateFoo(&p);
cout << p->a << endl;
delete p;
return 0;
}
Esto se imprimirá
12
Pero existen otros usos útiles, como en el siguiente ejemplo, para iterar una matriz de cadenas e imprimirlas en la salida estándar.
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
const char* words[] = { "first", "second", NULL };
for (const char** p = words; *p != NULL; ++p) {
cout << *p << endl;
}
return 0;
}
Otros consejos
En mi opinión, el uso más común es pasar referencia a una variable de puntero
void test(int ** var)
{
...
}
int *foo = ...
test(&foo);
Puede crear una matriz irregular multidimensional utilizando punteros dobles:
int ** array = new *int[2];
array[0] = new int[2];
array[1] = new int[3];
Un escenario común es cuando necesitas aprobar un nulo puntero a una función, inicializarlo dentro de esa función y usarlo fuera de la función.Sin indirección múltiple, la función que llama nunca tendría acceso al objeto inicializado.
Considere la siguiente función:
initialize(foo* my_foo)
{
my_foo = new Foo();
}
Cualquier función que llame a 'initialize(foo*)' no tendrá acceso a la instancia inicializada de foo, porque el puntero que se pasa a esta función es una copia.(Después de todo, el puntero es solo un número entero, y los números enteros se pasan por valor).
Sin embargo, si la función se definiera así:
initialize(foo** my_foo)
{
*my_foo = new Foo();
}
...y se llamaba así...
Foo* my_foo;
initialize(&my_foo);
...entonces la persona que llama tendría acceso a la instancia inicializada, a través de 'my_foo' - porque es el DIRECCIÓN del puntero que se pasó a 'inicializar'.
Por supuesto, en mi ejemplo simplificado, la función 'inicializar' podría simplemente devolver la instancia recién creada mediante la palabra clave return, pero eso no siempre es adecuado; tal vez la función necesite devolver algo más.
Si pasa un puntero como parámetro de salida, es posible que desee pasarlo como Foo**
y establecer su valor como *ppFoo = pSomeOtherFoo
.
Y desde el departamento de algoritmos y estructuras de datos, puede utilizar esa doble dirección indirecta para actualizar punteros, lo que puede ser más rápido que, por ejemplo, intercambiar objetos reales.
Un ejemplo simple sería usar int** foo_mat
como una matriz 2D de números enteros.O también puedes usar punteros a punteros - digamos que tienes un puntero void* foo
y tienes 2 objetos diferentes que tienen una referencia con los siguientes miembros: void** foo_pointer1
y void** foo_pointer2
, al tener un puntero a un puntero, realmente puede verificar si *foo_pointer1 == NULL
lo que indica que foo es NULL.No podrías comprobar si foo es NULL si foo_pointer1 fuera un puntero normal.Espero que mi explicación no haya sido demasiado complicada :)
carlos:Tu ejemplo debería ser:
*p = x;
(Tienes dos estrellas.) :-)
En C, el modismo es absolutamente necesario.Considere el problema en el que desea que una función agregue una cadena (C puro, por lo tanto un char *) a una matriz de punteros a char *.El prototipo de función requiere tres niveles de direccionamiento indirecto:
int AddStringToList(unsigned int *count_ptr, char ***list_ptr, const char *string_to_add);
Lo llamamos de la siguiente manera:
unsigned int the_count = 0;
char **the_list = NULL;
AddStringToList(&the_count, &the_list, "The string I'm adding");
En C++ tenemos la opción de usar referencias, lo que produciría una firma diferente.Pero todavía necesitamos los dos niveles de indirección que preguntaste en tu pregunta original:
int AddStringToList(unsigned int &count_ptr, char **&list_ptr, const char *string_to_add);
Normalmente, cuando pasas un puntero a una función como valor de retorno:
ErrorCode AllocateObject (void **object);
donde la función devuelve un código de error de éxito/fracaso y completa el parámetro del objeto con un puntero al nuevo objeto:
*object = new Object;
Esto se usa mucho en la programación COM en Win32.
Esto es más una cosa de C, en C++ a menudo puedes envolver este tipo de sistema en una clase para hacer que el código sea más legible.