09 мая 2012

#6 Указатели на указатели и ссылки на указатели

... или «чем глубже в лес, тем... темнее... помидоры??»



Указатели на указатели называются  #многоуровневыми .

Если объявлен массив указателей, то указатель на первый его элемент, будет указателем на указатель:
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;
правомочно, то
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;    // Так нельзя.



Об этом говорят специалисты: