A quoi sert plusieurs indirection en C ++?
Question
Dans quelles circonstances pouvez-vous utiliser plusieurs indirection (c'est-à-dire une chaîne de pointeurs comme dans Foo **
) en C ++?
La solution
Comme @aku l'a souligné, l'utilisation la plus courante consiste à autoriser la modification d'un paramètre de pointeur après le retour de la fonction.
#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;
}
Ceci imprimera
12
Mais il existe plusieurs autres utilisations utiles, comme dans l'exemple suivant, pour itérer un tableau de chaînes et les imprimer sur la sortie standard.
#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;
}
Autres conseils
L’utilisation la plus courante de l’OMI consiste à transmettre une référence à la variable de pointeur
void test(int ** var)
{
...
}
int *foo = ...
test(&foo);
Vous pouvez créer un tableau multidimensionnel en pointillés à l'aide de doubles pointeurs:
int ** array = new *int[2];
array[0] = new int[2];
array[1] = new int[3];
Un scénario courant consiste à passer un pointeur null à une fonction, à l'initialiser dans cette fonction et à l'utiliser en dehors de la fonction. Sans indirection multiple, la fonction appelante n’aurait jamais accès à l’objet initialisé.
Considérez la fonction suivante:
initialize(foo* my_foo)
{
my_foo = new Foo();
}
Toute fonction qui appelle "initialize (foo *)" n'aura pas accès à l'instance initialisée de Foo , car le pointeur transmis à cette fonction est une copie. (Le pointeur n’est finalement qu’un entier, et les entiers sont passés par valeur.)
Cependant, si la fonction a été définie comme ceci:
initialize(foo** my_foo)
{
*my_foo = new Foo();
}
... et ça s'appelait comme ça ...
Foo* my_foo;
initialize(&my_foo);
... alors l'appelant aurait accès à l'instance initialisée via "my_foo" - car il s'agit de l'adresse du pointeur qui a été transmis à "initialize".
Bien sûr, dans mon exemple simplifié, la fonction 'initialiser' pourrait simplement renvoyer l'instance nouvellement créée via le mot clé return, mais cela ne convient pas toujours - peut-être que la fonction doit renvoyer autre chose.
Si vous transmettez un pointeur en tant que paramètre de sortie, vous pouvez le transmettre sous la forme Foo **
et lui attribuer la valeur * ppFoo = pSomeOtherFoo
.
Et à partir du service des algorithmes et des structures de données, vous pouvez utiliser cette double indirection pour mettre à jour les pointeurs, ce qui peut être plus rapide que par exemple l'échange d'objets réels.
Un exemple simple consisterait à utiliser int ** foo_mat
en tant que tableau 2d d'entiers.
Ou vous pouvez également utiliser des pointeurs vers des pointeurs - disons que vous avez un pointeur void * foo
et que vous avez 2 objets différents qui y sont référencés avec les membres suivants: void ** foo_pointer1
et void ** foo_pointer2
, en ayant un pointeur sur un pointeur, vous pouvez réellement vérifier si * foo_pointer1 == NULL
indique que foo est NULL. Vous ne pourriez pas vérifier si foo est NULL si foo_pointer1 était un pointeur normal.
J'espère que mon explication n'était pas trop compliquée:)
Carl: votre exemple devrait être:
*p = x;
(Vous avez deux étoiles.): -)
En C, l’idiome est absolument nécessaire. Considérez le problème dans lequel vous voulez qu'une fonction ajoute une chaîne (C pur, donc un char *) à un tableau de pointeurs sur char *. Le prototype de fonction nécessite trois niveaux d’indirection:
int AddStringToList(unsigned int *count_ptr, char ***list_ptr, const char *string_to_add);
Nous l'appelons comme suit:
unsigned int the_count = 0;
char **the_list = NULL;
AddStringToList(&the_count, &the_list, "The string I'm adding");
En C ++, nous avons la possibilité d’utiliser des références, ce qui donnerait une signature différente. Mais nous avons toujours besoin des deux niveaux d'indirection que vous avez évoqués dans votre question initiale:
int AddStringToList(unsigned int &count_ptr, char **&list_ptr, const char *string_to_add);
Habituellement, lorsque vous passez un pointeur sur une fonction en tant que valeur de retour:
ErrorCode AllocateObject (void **object);
où la fonction renvoie un code d'erreur de réussite / échec et remplit le paramètre d'objet avec un pointeur sur le nouvel objet:
*object = new Object;
Ceci est beaucoup utilisé dans la programmation COM sous Win32.
C’est plus une chose à faire en C; en C ++, vous pouvez souvent envelopper ce type de système dans une classe pour rendre le code plus lisible.