2.3.3. Понятие описаний составных типов

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

// i - переменная типа int; p - указатель на тип int;

// r - ссылка на тип int

int i = 1024, *p = &i, &r = i;

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

Определение нескольких переменных

Весьма распространенное заблуждение полагать, что модификатор типа (* или &) применяется ко всем переменным, определенным в одном операторе. Частично причина в том, что между модификатором типа и объявляемым именем может находиться пробел.

int* p; // вполне допустимо, но может ввести в заблуждение

Данное определение может ввести в заблуждение потому, что создается впечатление, будто int* является типом каждой переменной, объявленной в этом операторе. Несмотря на внешний вид, базовым типом этого объявления является int, а не int*. Символ * — это модификатор типа p, он не имеет никакого отношения к любым другим объектам, которые могли бы быть объявлены в том же операторе:

int* p1, p2; // p1 - указатель на тип int; p2 - переменная типа int

Есть два общепринятых стиля определения нескольких переменных с типом указателя или ссылки. Согласно первому, модификатор типа располагается рядом с идентификатором:

int *p1, *p2; // p1 и p2 — указатели на тип int

Этот стиль подчеркивает, что переменная имеет составной тип. Согласно второму, модификатор типа располагается рядом с типом, но он определяет только одну переменную в операторе:

int* p1; // p1 - указатель на тип int

int* p2; // p2 - указатель на тип int

Этот стиль подчеркивает, что объявление определяет составной тип.

Нет никакого единственно правильного способа определения указателей и ссылок. Важно неукоснительно придерживаться выбранного стиля.

В этой книге используется первый стиль, знак * (или &) помещается рядом с именем переменной.

Указатели на указатели

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

Каждый уровень указателя отмечается собственным символом *. Таким образом, для указателя на указатель пишут **, для указателя на указатель на указатель — *** и т.д.

int ival = 1024;

int *pi = &ival; // pi указывает на переменную типа int

int **ppi = π // ppi указывает на указатель на переменную типа int

Здесь pi — указатель на переменную типа int, a ppi — указатель на указатель на переменную типа. Эти объекты можно было бы представить так:

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

cout << "The value of ival "

     << "direct value: " << ival << " "

     << "indirect value: " << *pi << " "

     << "doubly indirect value: " << **ppi << endl;

Эта программа выводит значение переменной ival тремя разными способами: сначала непосредственно, затем через указатель pi на тип int и наконец обращением к значению указателя ppi дважды, чтобы добраться до основного значения в переменной ival.

Ссылки на указатели

Ссылка — не объект. Следовательно, не может быть указателя на ссылку. Но поскольку указатель — это объект, вполне можно определить ссылку на указатель.

int i = 42;

int *p;      // p - указатель на тип int

int *&r = p; // r - ссылка на указатель p

r = &i;      // r ссылается на указатель;

             // присвоение &i ссылке r делает p указателем на i

*r = 0;      // обращение к значению r дает i, объект, на который

             // указывает p; изменяет значение i на 0

Проще всего понять тип r — прочитать определение справа налево. Ближайший символ к имени переменной (в данном случае & в &r) непосредственно влияет на тип переменной. Таким образом, становится ясно, что r является ссылкой. Остальная часть оператора объявления определяет тип, на который ссылается ссылка r. Следующий символ, в данном случае *, указывает, что тип r относится к типу указателя. И наконец, базовый тип объявления указывает, что r — это ссылка на указатель на переменную типа int.

Сложное объявление указателя или ссылки может быть проще понять, если читать его справа налево.

Упражнения раздела 2.3.3

Упражнение 2.25. Определите типы и значения каждой из следующих переменных:

(a) int* ip, &r = ip; (b) int i, *ip = 0; (c) int* ip, ip2;

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК