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