К чему несколько косвенности в C++?

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

8 ответов


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

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

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

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

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

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

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

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

Foo* my_foo;

initialize(&my_foo);

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

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


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

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


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


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

*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);

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

*object = new Object;

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

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