04 Point2D alpha;
05 Point2D beta;
06 Point2D *ptr;
07 ptr = α
08 ptr->setX(1.0);
09 ptr->setY(2.5);
10 ptr = β
11 ptr->setX(4.0);
12 ptr->setY(4.5);
13 ptr = 0;
14 return 0;
15 }
В этом примере используется класс Point2D из предыдущего подраздела. В строках 4 и 5 определяется два объекта типа Point2D. Эти объекты инициализируются в значение (0, 0) стандартным конструктором Point2D.
В строке 6 определяется указатель на объект Point2D. Для обозначения указателя здесь используется звездочка перед именем переменной. Поскольку мы не инициализируем указатель, он будет содержать произвольный адрес памяти. Эта ситуация изменяется в строке 7, в которой адрес alpha присваивается этому указателю. Унарный оператор & возвращает адрес памяти, где располагается объект. Адрес обычно представляет собой 32-битовое или 64-битовое целое число, задающее смещение объекта в памяти.
В строках 8 и 9 мы обращаемся к объекту alpha с помощью указателя ptr. Поскольку ptr является указателем, а не объектом, необходимо использовать оператор -> (стрелка) вместо оператора . (точка).
В строке 10 указателю присваивается адрес beta. С этого момента любая выполняемая нами операция с этим указателем будет воздействовать на объект beta.
В строке 13 указатель устанавливается в нулевое значение. С++ не имеет ключевого слова для представления указателя, который не ссылается ни на один объект; вместо этого мы используем значение 0 (или символическую константу NULL, которая разворачивается в 0). Попытка применения нулевого указателя приведет к краху приложения с выводом такого сообщения об ошибке, как «Segmentation fault» (ошибка сегментации), «General protection fault» (общая ошибка защиты) или «Bus error» (ошибка шины). Применяя отладчик, можно найти строку программного кода, которая приводит к краху.
В конце функции объект alpha содержит пару координат (1.0, 2.5), а объект beta — (4.0,4.5).
Указатели часто используются для хранения объектов, память для которых выделяется динамически с помощью оператора new. Используя жаргон С++ можно сказать, что эти объекты распределяются в «куче», в то время как локальные переменные (т.е. переменные, определенные внутри функции) хранятся в «стеке».
Ниже приводится фрагмент программного кода, иллюстрирующий динамическое распределение памяти при помощи оператора new:
01 #include "point2d.h"
02 int main
03 {
04 Point2D *point = new Point2D;
05 point->setX(1.0);
06 point->setY(2.5);
07 delete point;
08 return 0;
09 }
Оператор new возвращает адрес памяти для нового распределенного объекта. Мы сохраняем адрес в переменной указателя и обращаемся к объекту через этот указатель. Поработав с объектом, мы возвращаем занимаемую им память, используя оператор delete. В отличие от Java и C#, сборщик мусора отсутствует в С++; динамически распределяемые объекты должны явно освобождать занимаемую ими память при помощи оператора delete, когда они становятся больше ненужными. В главе 2 описывается механизм родственных связей Qt, который значительно упрощает управление памятью в программах, написанных на С++.
Если не вызвать оператор delete, память остается занятой до тех пор, пока не завершится программа. Это не создаст никаких проблем в приведенном выше примере, потому что память выделяется только для одного объекта, однако в программе, в которой постоянно создаются новые объекты, это может привести к нехватке машинной памяти. После удаления объекта переменная указателя по-прежнему будет хранить адрес объекта. Такой указатель является «повисшим указателем» и не должен использоваться для обращения к объекту. Qt предоставляет «умный» указатель QPointer, который автоматически устанавливает себя в 0, если удаляется объект QObject, на который он ссылается.
В приведенном выше примере мы вызывали стандартный конструктор и функции setX и setY для инициализации объекта. Вместо этого можно было использовать конструктор с двумя параметрами:
Point2D *point = new Point2D(1.0, 2.5);
Кроме того, мы могли бы распределить объект в стеке следующим образом:
Point2D point;
point.setX(1.0);
point.setY(2.5);
Распределенные таким образом объекты автоматически освобождаются в конце блока, в котором они появляются.
Если мы не собираемся модифицировать объект при помощи указателя, можно объявить указатель как константный. Например:
const Point2D *ptr = new Point2D(1.0, 2.5);
double x = ptr->x;
double у = ptr->y;