17.4.4. Инициализация
Как всегда, мы хотели бы, чтобы объект уже имел какое-то значение, прежде чем мы приступим к его использованию; иначе говоря, мы хотели бы, чтобы указатели и объекты, на которые они ссылаются, были инициализированы. Рассмотрим пример.
double* p0; // объявление без инициализации:
// возможны проблемы
double* p1 = new double; // выделение памяти для переменной
// типа double
// без инициализации
double* p2 = new double(5.5); // инициализируем переменную типа
double
// числом 5.5
double* p3 = new double[5]; // выделение памяти для массива
// из пяти чисел
// типа double без инициализации
Очевидно, что объявление указателя p0
без инициализации может вызвать проблемы. Рассмотрим пример.
*p0 = 7.0;
7.0
в некую ячейку памяти. Мы не знаем, в какой части памяти расположена эта ячейка. Это может быть безопасно, но рассчитывать на это нельзя. Рано или поздно мы получим тот же результат, что и при выходе за пределы допустимого диапазона: программа завершит работу аварийно или выдаст неправильные результаты. Огромное количество серьезных проблем в программах, написанных в старом стиле языка С, вызвано использованием неинициализированных указателей и выходом за пределы допустимого диапазона. Мы должны делать все, чтобы избежать таких проблем, частично потому, что наша цель — профессионализм, а частично потому, что мы не хотим терять время в поисках ошибок такого рода.
p2: *p2
равно 5.5
. Обратите внимание на круглые скобки, ()
, используемые при инициализации. Не перепутайте их с квадратными скобками, []
, которые используются для индикации массивов.
В языке С++ нет средства для инициализации массивов объектов встроенных типов, память для которых выделена оператором new
. Для массивов работу придется проделать самостоятельно. Рассмотрим пример.
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; // нулевой указатель
Указатель, которому присвоен нуль, называют
if (p0 != 0) // проверка корректности указателя p0