Для чего используется множественное косвенное обращение в C ++? - PullRequest
10 голосов
/ 16 сентября 2008

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

Ответы [ 8 ]

12 голосов
/ 16 сентября 2008

Самое распространенное использование, как указывало @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;
}
7 голосов
/ 16 сентября 2008

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

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

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

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

int ** array = new *int[2];
array[0] = new int[2];
array[1] = new int[3];
5 голосов
/ 16 сентября 2008

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

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

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

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

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

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

... и назывался так ...

Foo* my_foo;

initialize(&my_foo);

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

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

3 голосов
/ 16 сентября 2008

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

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

1 голос
/ 16 сентября 2008

В Си идиома абсолютно обязательна. Рассмотрим проблему, в которой вы хотите, чтобы функция добавляла строку (чистый 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");

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

int AddStringToList(unsigned int &count_ptr, char **&list_ptr, const char *string_to_add);
1 голос
/ 16 сентября 2008

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

*p = x;

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

1 голос
/ 16 сентября 2008

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

ErrorCode AllocateObject (void **object);

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

*object = new Object;

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

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

1 голос
/ 16 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...