
Int main()
{
using namespace std;
int nights = 1001;
int * pt = new int; // allocate space for an int
*pt = 1001; // store a value there
cout << "nights value = ";
cout << nights << ": location " << &nights << endl;
cout << "int ";
cout << "value = " << *pt << ": location = " << pt << endl;
double * pd = new double;
// allocate space for a double
*pd = 10000001.0; // store a double there
cout << "double ";
cout << "value = " << *pd << ": location = " << pd << endl;
cout << "location of pointer pd: " << &pd << endl;
cout << "size of pt = " << sizeof(pt);
cout << ": size of *pt = " << sizeof(*pt) << endl;
cout << "size of pd = " << sizeof pd;
cout << ": size of *pd = " << sizeof(*pd) << endl;
cin.get();
return 0;
}
Ниже показан вывод программы из листинга 4.17:
Естественно, точные значения адресов памяти будут варьироваться от системы к системе.
Замечания по программе
Программа в листинге 4.17 использует операцию new для выделения памяти под объекты данных типа int и типа double. Это происходит во время выполнения программы. Указатели pt и pd указывают на эти объекты данных. Без них вы не смогли бы получить к ним доступ. С ними же вы можете применять *pt и *pd подобно тому, как вы используете обычные переменные. Вы присваиваете значения новым объектам данных, присваивая их *pt и *pd. Аналогично вы посылаете на печать *pt и *pd, чтобы отобразить эти значения.
Программа в листинге 4.17 также демонстрирует одну из причин того, что необходимо объявить тип данных, на которые указывает указатель. Сам по себе адрес относится только к началу сохраненного объекта, он не включает информации о типе или размере. К тому же обратите внимание, что указатель на int имеет тот же размер, что и указатель на double. Оба они являются адресами. Но поскольку объявлены типы указателей, программа знает, что *pd имеет тип double размером в 8 байт, в то время как *pt представляет собой значение типа int размером в 4 байта. Когда программа печатает значение *pd, cout известно, сколько байт нужно прочитать и как их интерпретировать.
Еще один момент, который следует отметить, состоит в том, что обычно операция new использует другие блоки памяти, чем применяемые ранее объявления обычных переменных.
Освобождение памяти с помощью операции delete
Использование операции new для запрашивания памяти, когда она нужна — одна из сторон пакета управления памятью C++. Второй стороной является операция delete, которая позволяет вернуть память в пул свободной памяти, когда работа с ней завершена. Это — важный шаг к максимально эффективному использованию памяти. Память, которую вы возвращаете, или освобождаете, затем может быть повторно использована другими частями программы. Операция delete применяется с указателем на блок памяти, который был выделен операцией new:
int * ps = new int;
// выделить память с помощью операции new
…. // использовать память
delete ps; // по завершении освободить память
// с помощью операции delete
Это освобождает память, на которую указывает ps, но не удаляет сам указатель ps. Вы можете повторно использовать ps — например, чтобы указать на другой выделенный new блок памяти. Вы всегда должны обеспечивать сбалансированное применение new и delete; в противном случае вы рискуете столкнуться с таким явлением, как утечка памяти, т.е. ситуацией, когда память выделена, но более не может быть использована. Если утечки памяти слишком велики, то попытка программы выделить очередной блок может привести к ее аварийному завершению.
Вы не должны пытаться освобождать блок памяти, который уже был однажды освобожден. Стандарт C++ гласит, что результат таких попыток не определен, а это значит, что последствия могут оказаться любыми. Кроме того, вы не можете с помощью операции delete освобождать память, которая была выделена посредством объявления обычных переменных:
Операция delete должна использоваться только для освобождения памяти, выделенной с помощью new.
Обратите внимание, что обязательным условием применения операции delete является использование ее с памятью, выделенной операцией new. Это не значит, что вы обязаны применять тот же указатель, который был использован с new - просто нужно задать тот же адрес:
int * ps = new int; // выделение памяти
int * pq = ps; // установка второго указателя на тот же блок
delete pq; // вызов delete для второго указателя
Обычно не стоит создавать два указателя на один и тот же блок памяти, т.к. это может привести к ошибочной попытке освобождения одного и того же блока дважды. Но, как вы вскоре убедитесь, применение второго указателя оправдано, когда вы работаете с функциями, возвращающими указатель.