Для чего нужна множественная косвенность в C++?

StackOverflow https://stackoverflow.com/questions/71108

  •  09-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

Наиболее распространенное использование, как указал @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;
}

Другие советы

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

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

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

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

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

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

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

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

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

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

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

...и это называлось так...

Foo* my_foo;

initialize(&my_foo);

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

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

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

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

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

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

*p = x;

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

В 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");

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

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

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

ErrorCode AllocateObject (void **object);

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

*object = new Object;

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top