Для чего используется множественная косвенность в С++?

В каких обстоятельствах вы можете использовать множественную косвенность (то есть цепочку указателей, как в Foo **) в С++?

Ответ 1

Наиболее распространенное использование, как указано в @aku, заключается в том, чтобы разрешить изменение указательного параметра после того, как функция вернется.

#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;
}

Откроется

12

Но есть несколько других полезных применений, как в следующем примере, для итерации массива строк и печати их на стандартный вывод.

#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;
}

Ответ 2

Наиболее распространенным использованием IMO является передача ссылки на переменную-указатель

void test(int ** var)
{
 ...
}

int *foo = ...
test(&foo);

Вы можете создать многомерный зубчатый массив, используя двойные указатели:

int ** array = new *int[2];
array[0] = new int[2];
array[1] = new int[3];

Ответ 3

Один общий сценарий - это то, где вам нужно передать указатель null на функцию и инициализировать его внутри этой функции и использовать вне функции. Без множественной косвенности вызывающая функция никогда не получит доступ к инициализированному объекту.

Рассмотрим следующую функцию:

initialize(foo* my_foo)
{
    my_foo = new Foo();
}

Любая функция, вызывающая 'initialize (foo *)', не будет иметь доступ к инициализированному экземпляру Foo, beacuse указатель, который передается этой функции, является копией. (В конце концов, указатель является целым числом, а целые числа передаются по значению.)

Однако, если функция была определена следующим образом:

initialize(foo** my_foo)
{
    *my_foo = new Foo();
}

... и он называется так...

Foo* my_foo;

initialize(&my_foo);

... тогда у вызывающего будет доступ к инициализированному экземпляру через 'my_foo' - потому что это адрес указателя, который был передан в 'initialize'.

Конечно, в моем упрощенном примере функция "initialize" может просто вернуть вновь созданный экземпляр с помощью ключевого слова return, но это не всегда подходит - возможно, функция должна вернуть что-то еще.

Ответ 4

Если вы передадите указатель в качестве выходного параметра, вы можете передать его как Foo** и установить его значение как *ppFoo = pSomeOtherFoo.

И из отдела алгоритмов и данных-структур вы можете использовать эту двойную косвенность для обновления указателей, которая может быть быстрее, чем, например, свопинг фактических объектов.

Ответ 5

Простым примером будет использование int** foo_mat как 2d массива целых чисел. Или вы также можете использовать указатели для указателей - скажем, что у вас есть указатель void* foo, и у вас есть два разных объекта, которые имеют ссылку на него со следующими членами: void** foo_pointer1 и void** foo_pointer2, указав указатель на указатель, вы можете проверить, есть ли *foo_pointer1 == NULL, который указывает, что foo имеет значение NULL. Вы не смогли бы проверить, является ли foo NULL, если foo_pointer1 был регулярным указателем. Я надеюсь, что мое объяснение не было слишком грязным:)

Ответ 6

Обычно, когда вы передаете указатель на функцию как возвращаемое значение:

ErrorCode AllocateObject (void **object);

где функция возвращает код ошибки успеха/отказа и заполняет параметр объекта указателем на новый объект:

*object = new Object;

Это много используется в программировании COM в Win32.

Это больше похоже на C, на С++ вы можете часто превращать этот тип системы в класс, чтобы сделать код более удобочитаемым.

Ответ 7

Карл: Ваш пример должен быть:

*p = x;

(У вас две звезды.): -)

Ответ 8

В C требуется абсолютная идиома. Рассмотрим проблему, в которой вы хотите, чтобы функция добавляла строку (чистый C, а затем char *) в массив указателей на char *. Прототип функции требует трех уровней косвенности:

int AddStringToList(unsigned int *count_ptr, char ***list_ptr, const char *string_to_add);

Назовем это следующим образом:

unsigned int   the_count = 0;
char         **the_list  = NULL;

AddStringToList(&the_count, &the_list, "The string I'm adding");

В С++ у нас есть возможность использовать ссылки вместо этого, что даст другую подпись. Но нам еще нужны два уровня косвенности, о которых вы спрашивали в своем первоначальном вопросе:

int AddStringToList(unsigned int &count_ptr, char **&list_ptr, const char *string_to_add);