Para que serve a indireção múltipla em C++?
Pergunta
Sob quais circunstâncias você pode querer usar indireção múltipla (ou seja, uma cadeia de ponteiros como em Foo **
) em C++?
Solução
O uso mais comum, como @aku apontou, é permitir que uma alteração em um parâmetro de ponteiro fique visível após o retorno da função.
#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;
}
Isto irá imprimir
12
Mas existem vários outros usos úteis, como no exemplo a seguir, para iterar uma matriz de strings e imprimi-las na saída padrão.
#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;
}
Outras dicas
O uso mais comum da IMO é passar referência para variável de ponteiro
void test(int ** var)
{
...
}
int *foo = ...
test(&foo);
Você pode criar uma matriz irregular multidimensional usando ponteiros duplos:
int ** array = new *int[2];
array[0] = new int[2];
array[1] = new int[3];
Um cenário comum é onde você precisa passar por um nulo ponteiro para uma função e inicializá-lo dentro dessa função e usá-lo fora da função.Sem indireção múltipla, a função de chamada nunca teria acesso ao objeto inicializado.
Considere a seguinte função:
initialize(foo* my_foo)
{
my_foo = new Foo();
}
Qualquer função que chame 'initialize(foo*)' não terá acesso à instância inicializada de Foo, porque o ponteiro passado para esta função é uma cópia.(Afinal, o ponteiro é apenas um número inteiro e os números inteiros são passados por valor.)
No entanto, se a função foi definida assim:
initialize(foo** my_foo)
{
*my_foo = new Foo();
}
...e foi chamado assim...
Foo* my_foo;
initialize(&my_foo);
...então o chamador teria acesso à instância inicializada, via 'my_foo' - porque é o endereço do ponteiro que foi passado para 'inicializar'.
É claro que, no meu exemplo simplificado, a função 'initialize' poderia simplesmente retornar a instância recém-criada através da palavra-chave return, mas isso nem sempre é adequado - talvez a função precise retornar outra coisa.
Se você passar um ponteiro como parâmetro de saída, você pode querer passá-lo como Foo**
e defina seu valor como *ppFoo = pSomeOtherFoo
.
E do departamento de algoritmos e estruturas de dados, você pode usar essa dupla indireção para atualizar ponteiros, o que pode ser mais rápido do que, por exemplo, trocar objetos reais.
Um exemplo simples seria usar int** foo_mat
como uma matriz 2d de inteiros.Ou você também pode usar ponteiros para ponteiros - digamos que você tenha um ponteiro void* foo
e você tem 2 objetos diferentes que fazem referência a ele com os seguintes membros: void** foo_pointer1
e void** foo_pointer2
, tendo um ponteiro para um ponteiro, você pode realmente verificar se *foo_pointer1 == NULL
o que indica que foo é NULL.Você não seria capaz de verificar se foo é NULL se foo_pointer1 fosse um ponteiro normal.Espero que minha explicação não tenha sido muito confusa :)
Carlos:Seu exemplo deveria ser:
*p = x;
(Você tem duas estrelas.) :-)
Em C, o idioma é absolutamente necessário.Considere o problema no qual você deseja que uma função adicione uma string (C puro, portanto um char *) a uma matriz de ponteiros para char *.O protótipo da função requer três níveis de indireção:
int AddStringToList(unsigned int *count_ptr, char ***list_ptr, const char *string_to_add);
Chamamos isso da seguinte forma:
unsigned int the_count = 0;
char **the_list = NULL;
AddStringToList(&the_count, &the_list, "The string I'm adding");
Em C++ temos a opção de usar referências, o que geraria uma assinatura diferente.Mas ainda precisamos dos dois níveis de indireção que você perguntou na sua pergunta original:
int AddStringToList(unsigned int &count_ptr, char **&list_ptr, const char *string_to_add);
Normalmente, quando você passa um ponteiro para uma função como valor de retorno:
ErrorCode AllocateObject (void **object);
onde a função retorna um código de erro de sucesso/falha e preenche o parâmetro do objeto com um ponteiro para o novo objeto:
*object = new Object;
Isso é muito usado na programação COM no Win32.
Isso é mais uma coisa de C, em C++ muitas vezes você pode agrupar esse tipo de sistema em uma classe para tornar o código mais legível.