... или «чем глубже в лес, тем... темнее... помидоры??»
Указатели на указатели называются #многоуровневыми .
Если объявлен массив указателей, то указатель на первый его элемент, будет указателем на указатель:
int* _array[10];
int** pa = _array;
Иногда функции принимают указатели на указатели, чтобы изменить адрес, на который какой-то указатель ссылается. Такой пример работы очень плох. Но запомните его, чтобы уметь разобрать такое в чужом коде, а заодно и посмеяться над ним:
...
void func(int** _ppi);
int* pi = new int(17);
// ...
delete pi;
func(&pi, 99);
void func(int** _ppi, int _iVal) {
*_ppi = new int(_iVal);
}
Пример бесполезный. Зато демонстрирует работу.
Этот же пример можно переписать с помощью ссылки. Так будет понятнее. Обратите внимание, что смысл объявляемого читается справа налево:
void func(int*& _rpi, int _iVal) {
_rpi = new int(_iVal);
}
int*& _rpi — ссылка на указатель на int.
Cat** ppCat = &(new Cat);
Mammal** ppMammal = ppCat;
— нет. Почему? В теме #13 Отсутствие автоматического преобразования указателей на указатель в указатель на указатель есть объяснение.
2) Обычные указатели на неконстанту могут быть преобразованы в указатели на константу. С многоуровневыми указателями это проделать невозможно:
int* pi = new int(3);
const int* pci = pi; // Всё правильно.
int** ppi = &(new int(3));
const int** ppci = ppi; // Так нельзя.
Если объявлен массив указателей, то указатель на первый его элемент, будет указателем на указатель:
int* _array[10];
int** pa = _array;
Иногда функции принимают указатели на указатели, чтобы изменить адрес, на который какой-то указатель ссылается. Такой пример работы очень плох. Но запомните его, чтобы уметь разобрать такое в чужом коде, а заодно и посмеяться над ним:
...
void func(int** _ppi);
int* pi = new int(17);
// ...
delete pi;
func(&pi, 99);
void func(int** _ppi, int _iVal) {
*_ppi = new int(_iVal);
}
Пример бесполезный. Зато демонстрирует работу.
Этот же пример можно переписать с помощью ссылки. Так будет понятнее. Обратите внимание, что смысл объявляемого читается справа налево:
void func(int*& _rpi, int _iVal) {
_rpi = new int(_iVal);
}
int*& _rpi — ссылка на указатель на int.
Преобразования, применимые к обычным указателям, не применимы к многоуровневым:
1) Класс Cat наследуется от класса Mammal. И если
Cat* pCat = new Cat;
Mammal* pMammal = pCat;
правомочно, то
Mammal** ppMammal = ppCat;
— нет. Почему? В теме #13 Отсутствие автоматического преобразования указателей на указатель в указатель на указатель есть объяснение.
2) Обычные указатели на неконстанту могут быть преобразованы в указатели на константу. С многоуровневыми указателями это проделать невозможно:
int* pi = new int(3);
const int* pci = pi; // Всё правильно.
int** ppi = &(new int(3));
const int** ppci = ppi; // Так нельзя.
Об этом говорят специалисты: