К чему несколько косвенности в 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 был обычным указателем.
Я надеюсь, что мое объяснение не было слишком грязно:)
В 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++ вы часто можете обернуть этот тип системы в класс, чтобы сделать код более читаемым.