Чем удобно размещение объектов в динамической области памяти?
Объекты, сохраненные в области динамического обмена, не уничтожаются при выходе из функции, в которой они были объявлены. Кроме того, это дает возможность уже в процессе выполнения программы решать, какое количество объектов требуется объявить. Более подробно этот вопрос обсуждается на следующем занятии.
Зачем ограничивать права доступа к объекту, объявляя его константным?
Следует использовать все средства, позволяющие предотвратить появление ошибок. На практике достаточно сложно отследить, в какой момент и какой функцией изменяется объект. Использование спецификатора const позволяет решить эту проблему.
Коллоквиум
В этом разделе предлагаются вопросы для самоконтроля и укрепления полученных знаний, а также рассматривается ряд упражнений, которые помогут закрепить ваши практические навыки. Попытайтесь самостоятельно ответить на вопросы теста и выполнить задания, а потом сверьте полученные результаты с ответами в приложении Г. Не приступайте к изучению материала следующей главы, если для вас остались неясными хотя бы некоторые из предложенных ниже вопросов.
Контрольные вопросы
1. Какой оператор используется для получения адреса переменной?
2. Какой оператор позволяет получить значение, записанное по адресу, содержащемуся в указателе?
3. Что такое указатель?
4. В чем различие между адресом, который хранится в указателе, и значением, записанным по этому адресу?
5. В чем различие между оператором разыменования и оператором получения адреса?
6. В чем различие между следующими объявлениями: const int * ptrOne и int * const ptrTwo?
Упражнения
1. Объясните смысл следующих объявлений переменных:
• int * pOne
• int vTwo
• int * pThree = &vTwo
2. Допустим, в программе объявлена переменная yourAge типа unsigned short. Как объявить указатель, позволяющий манипулировать этой переменной?
3. С помошью указателя присвойте переменной yourAge значение 50.
4. Напишите небольшую программу и объявите в ней переменную типа int и указатель на этот тип. Сохраните адрес переменной в указателе. Используя указатель, присвойте переменной какое-либо значение,
5. Жучки: найдите ошибку в следующем фрагменте программы.
#include <iostream,h>
int main()
{
int *pInt;
*pInt = 9;
cout << "The value at pInt: " << *pInt;
return 0;
}
6. Жучки: найдите ошибку в следующем фрагменте программы.
int main()
{
int SomeVariable = 5;
cout << "SomeVariable: " << SomeVariable << "n";
int *pVar = & SomeVariable;
pVar = 9;
cout << "SomeVariable: " << *pVar << "n";
return 0;
}
День 9-й. Ссылки
На предыдущем занятии вы узнали, как пользоваться указателями для управления объектами в свободной памяти и как ссылаться на эти объекты косвенным образом. Ссылки, которые обсуждаются на этом занятии, обладают почти теми же возможностями, что и указатели, но при более простом синтаксисе. Сегодня вы узнаете:
• Что представляют собой ссылки
• Чем ссылки отличаются ог указателей
• Как создать ссылки и использовать их
• Какие ограничения есть у ссылок
• Как по ссылке передаются значения и объекты в функции и из функций
Что такое ссылка
Ссылка — это то же, что и псевдоним. При создании ссылки мы инициализируем ее с помощью имени другого объекта, адресата. С этого момента ссылка действует как альтернативное имя данного объекта, поэтому все, что делается со ссылкой, в действительности происходит с этим объектом.
Для объявления ссылки нужно указать типа объекта адресата, за которым следует оператор ссылки (&), а за ним — имя ссылки. Для ссылок можно использовать любое легальное имя переменной, но многие программисты предпочитают со всеми именами ссылок использовать префикс "г". Так, если у вас есть целочисленная переменная с именем someInt, вы можете создать ссылку на эту переменную, написав
int &rSomeRef = someInt;
Это читается следующим образом: rSomeRef — это ссылка на целочисленное значение, инициализированная адресом переменной someInt. Создание и использование ссылок показано в листинге 9.1.
Примечание:Обратите внимание на то, что оператор ссылки (&) выглядит так же, как оператор адреса, который используется для возвращения адреса при работе с указателями. Однако это не одинаковые операторы, хотя очевидно, что они родственны.
Листинг 9.1. Создание и использование ссылок
1: // Листинг 9.1.
2: // Пример использования ссылок
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int intOne;
9: int &rSomeRef = intOne;
10:
11: intOne = 5;
12: cout << "intOne: " << intOne << endl;
13: cout << "rSomeRef: " << rSomeRef << endl;
14:
15: rSomeRef = 7;
16: cout << "intOne: " << intOne << endl;
17: cout << "rSomeRef: " << rSomeRef << endl;
18: return 0;
19: }
Результат:
intOne: 5
rSomeRef: 5
intOne: 7
rSomeRef: 7
Анализ: В строке 8 объявляется локальная целочисленная переменная intOne, а в строке 9 — ссылка rSomeRef на некоторое целое значение, инициализируемая адресом переменной intOne. Если объявить ссылку, но не инициализировать ее, будет сгенерирована ошибка компиляции. Ссылки, в отличие от указателя, необходимо инициализировать при объявлении.
В строке 11 переменной intOne присваивается значение 5. В строках 12 и 13 выводятся на экран значения переменной intOne и ссылки rSomeRef - они, конечно же, оказываются одинаковыми.
В строке 15 ссылке rSomeRef присваивается значение 7. Поскольку мы имеем дело со ссылкой, а она является псевдонимом для переменной intOne, то число 7 в действительности присваивается переменной intOne, что и подтверждается выводом на экран в строках 16 и 17.
Использование оператора адреса (&) при работе со ссылками
Если использовать ссылку для получения адреса, она вернет адрес своего адресата. В этом и состоит природа ссылок. Они являются псевдонимами для своих адресатов. Именно это свойство и демонстрирует листинг 9.2.
Листинг 9.2. Взятие адреса ссылки
1: // Листинг 9.2.
2: // Пример использования ссылок
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int intOne;
9: int &rSomeRef = intOne;
10:
11: intOne = 5;
12: cout << "intOne: " << intOne << endl;
13: cout << "rSomeRef: " << rSomeRef << endl;
14:
15: cout << "&intOne: " << &mtOne << endl;
16: cout << "&rSomeRef: " << &rSomeRef << endl;
17:
18: return 0;
19: }
Результат:
intOne: 5
rSomeRef: 5
&intOne: 0x3500
&rSomeRef: 0x3500
Примечание:Результаты работы программы на вашем компьютере могут отличаться от приведенных в последних двух строках.
Анализ: И вновь-таки ссылка rSomeRef инициализируется адресом переменной intOno. На этот раз выводятся адреса двух переменных, и они оказываются идентичными. В языке C++ не предусмотрено предоставление доступа к адресу самой ссылки, поскольку в этом нет смысла. С таким же успехом для этого можно было бы использовать указатель или другую переменную. Ссылки инициализируются при создании, и они всегда действуют как синонимы для своих адресатов, даже в том случае, когда применяется оператор адреса.
Например, если у вас есть класс с именем City, вы могли бы объявить объект этого класса следующим образом:
City boston;
Затем можно объявить ссылку на некоторый объект класса City и инициализировать ее, используя данный конкретный объект:
City &beanTown = boston;
Существует только один класс City; оба идентификатора ссылаются на один и тот же объект одного и того же класса. Любое действие, которое вы предпримите относительно ссылки beanTown, будет выполнено также и над объектом boston.
Обратите внимание на различие между символом & в строке 9 листинга 9.2, который объявляет ссылку rSomeRef на значение типа int, и символами & в строках 15 и 16, которые возвращают адреса целочисленной переменной intOne и ссылки rSomeRef.
Обычно для ссылки оператор адреса не используется. Мы просто используем ссылку вместо связанной с ней переменной, как показано в строке 13.
Ссылки нельзя переназначать
Даже опытных программистов, которые хорошо знают правило о том, что ссылки нельзя переназначать и что они всегда являются псевдонимами для своих адресатов, может ввести в заблуждение происходящее при попытке переназначить ссылку. То, что кажется переназначением, оказывается присвоением нового значения адресату. Этот факт иллюстрируется в листинге 9.3.