Литмир - Электронная Библиотека
A
A

double* p4 = new double[5];

for (int i = 0; i<5; ++i) p4[i] = i;

Теперь указатель

p4
ссылается на объекты типа
double
, содержащие числа
0.0
,
1.0
,
2.0
,
3.0
и
4.0
.

Как обычно, мы должны избегать неинициализированных объектов и следить за тем, чтобы они получили значения до того, как будут использованы. Компиляторы часто имеют режим отладки, в котором они по умолчанию инициализируют все переменные предсказуемой величиной (обычно нулем). Это значит, что, когда вы отключаете режим отладки, чтобы отправить программу заказчику, запускаете оптимизатор или просто компилируете программу на другой машине, программа, содержащая неинициализированные переменные, может внезапно перестать работать правильно. Не используйте неинициализированные переменные. Если класс

X
имеет конструктор по умолчанию, то получим следующее:

X* px1 = new X;     // один объект класса Х, инициализированный

                    // по умолчанию

X* px2 = new X[17]; // 17 объектов класса Х, инициализированных

                    // по умолчанию

Если класс

Y
имеет конструктор, но не конструктор по умолчанию, мы должны выполнить явную инициализацию

Y* py1 = new Y;     // ошибка: нет конструктора по умолчанию

Y* py2 = new Y[17]; // ошибка: нет конструктора по умолчанию

Y* py3 = new Y(13); // OK: инициализирован адресом объекта Y(13)

17.4.5. Нулевой указатель

 Если в вашем распоряжении нет другого указателя, которым можно было бы инициализировать ваш указатель, используйте

0
(нуль).

double* p0 = 0; // нулевой указатель

Указатель, которому присвоен нуль, называют нулевым (null pointer). Корректность указателя (т.е. ссылается ли он на что-либо) часто проверяется с помощью сравнения его с нулем. Рассмотрим пример.

if (p0 != 0) // проверка корректности указателя p0

Этот тест неидеален, поскольку указатель

p0
может содержать случайное ненулевое значение или адрес объекта, который был удален с помощью оператора
delete
(подробнее об этом — в разделе 17.4.6). Однако такая проверка часто оказывается лучшим, что можно сделать. Мы не обязаны явно указывать нуль, поскольку инструкция
if
по умолчанию проверяет, является ли условие ненулевым.

if (p0) // проверка корректности указателя p0; эквивалентно p0!=0

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Мы предпочитаем более короткую форму проверки, полагая, что она точнее отражает смысл выражения “
p0
корректен”, но это дело вкуса.

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

17.4.6. Освобождение свободной памяти

Оператор

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

double* calc(int res_size, int max) // утечка памяти

{

  double* p = new double[max];

  double* res = new double[res_size];

  // используем указатель p для вычисления результатов

  // и записи их в массив res

  return res;

}

double* r = calc(100,1000);

В соответствии с этой программой каждый вызов функции

calc()
будет забирать из свободной памяти участок, размер которого равен размеру типа
double
, и присваивать его адрес указателю
p
. Например, вызов
calc(100,1000)
сделает недоступным для остальной части программы участок памяти, на котором могут разместиться тысяча переменных типа
double
.

Оператор, возвращающий освобождающую память, называется

delete
. Для того чтобы освободить память для дальнейшего использования, оператор
delete
следует применить к указателю, который был возвращен оператором
new
. Рассмотрим пример.

double* calc(int res_size, int max)

 // за использование памяти, выделенной для массива res,

 // несет ответственность вызывающий модуль

{

  double* p = new double[max];

  double* res = new double[res_size];

  // используем указатель p для вычисления результатов и их

  // записи в res

  delete[ ] p; // эта память больше не нужна: освобождаем ее

  return res;

}

 double* r = calc(100,1000);

 // используем указатель r

 delete[ ] r; // эта память больше не нужна: освобождаем ее

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

Оператор

delete
имеет две формы:

226
{"b":"847443","o":1}