Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 17 18 19 20 21 22 23 24 25 ... 297
Перейти на страницу:

Упражнение 2.20. Что делает следующая программа?

int i = 42;

int *p1 = &i;

*p1 = *p1 * *p1;

Упражнение 2.21. Объясните каждое из следующих определений. Укажите, все ли они корректны и почему.

int i = 0;

(a) double* dp = &i; (b) int *ip = i; (c) int *p = &i;

Упражнение 2.22. С учетом того, что p является указателем на тип int, объясните следующий код:

if (p) // ...

if (*p) // ...

Упражнение 2.23. Есть указатель p, можно ли определить, указывает ли он на допустимый объект? Если да, то как? Если нет, то почему?

Упражнение 2.24. Почему инициализация указателя p допустима, а указателя lp нет?

int i = 42; void *p = &i; long *lp = &i;

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 ivaln"

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

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

     << "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;

2.4. Спецификатор const

Иногда необходимо определить переменную, значение которой, как известно, не может быть изменено. Например, можно было бы использовать переменную, хранящую размер буфера. Использование переменной облегчит изменение размера буфера, если мы решим, что исходный размер нас не устраивает. С другой стороны, желательно предотвратить непреднамеренное изменение в коде значения этой переменной. Значение этой переменной можно сделать неизменным, используя в ее определении спецификатор const (qualifier const):

const int bufSize = 512; // размер буфера ввода

Это определит переменную bufSize как константу. Любая попытка присвоить ей значение будет ошибкой:

bufSize = 512; // ошибка: попытка записи в константный объект

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

const int i = get_size(); // ok: инициализация во время выполнения

const int j = 42;         // ok: инициализация во время компиляции

const int k;              // ошибка: k - неинициализированная константа

Инициализация и константы

Как уже упоминалось не раз, тип объекта определяет операции, которые можно с ним выполнять. Константный тип можно использовать для большинства, но не для всех операций, как и его неконстантный аналог. Ограничение одно — можно использовать только те операции, которые неспособны изменить объект. Например, тип const int можно использовать в арифметических выражениях точно так же, как обычный неконстантный тип int. Тип const int преобразуется в тип bool тем же способом, что и обычный тип int, и т.д.

К операциям, не изменяющим значение объекта, относится инициализация. При использовании объекта для инициализации другого объекта не имеет значения, один или оба из них являются константами.

int i = 42;

const int ci = i; // ok: значение i копируется в ci

int j = ci;       // ok: значение ci копируется в j

Хотя переменная ci имеет тип const int, ее значение имеет тип int. Константность переменной ci имеет значение только для операций, которые могли бы изменить ее значение. При копировании переменной ci для инициализации переменной j ее константность не имеет значения. Копирование объекта не изменяет его. Как только копия сделана, у нового объекта нет никакой дальнейшей связи с исходным объектом.

1 ... 17 18 19 20 21 22 23 24 25 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии