Как вы уже знаете, если ваша программа объявляет массив, компилятор C++ распределяет память для хранения его элементов. Однако представляется возможным, что до некоторого времени размер массива может быть не так велик, чтобы вместить все необходимые данные. Например, предположим, что вы создали массив для хранения 100 акций. Если позже вам потребуется хранить более 100 акций, вы должны изменить свою программу и перекомпилировать ее. С другой стороны, вместо распределения массива фиксированного размера ваши программы могут запрашивать необходимое количество памяти динамически, т.е. во время выполнения. Например, если программе необходимо следить за акциями, она могла бы запросить память, достаточную для хранения 100 акций. Аналогично, если программе необходимы только 25 акций, она могла бы запросить меньше памяти. Распределяя подобным образом память динамически, ваши программы непрерывно изменяют свои потребности без дополнительного программирования. Если ваши программы запрашивают память во время выполнения, они указывают требуемое количество памяти, а C++ возвращает указатель на эту память. C++ распределяет память из областей памяти, которые называются свободной памятью. В этом уроке рассматриваются действия, которые должна выполнить ваша программа для динамического распределения, а впоследствии освобождения памяти во время выполнения. К концу данного урока вы освоите следующие основные концепции:

  • Чтобы запросить память во время выполнения, ваши программы должны использовать оператор C++ new.
  • При использовании оператораnew программы указывают количество требуемой памяти. Если операторnew может успешно выделить требуемый объем памяти, он возвращает указатель на начало области выделенной памяти.
  • Если операторnew не может удовлетворить запрос на память вашей программы (возможно, свободной памяти уже не осталось), он возвращает указатель NULL.
  • Чтобы позже освободить память, распределенную с помощью оператораnew, ваши программы должны использовать оператор C++ delete.

Динамическое распределение памяти во время выполнения является чрезвычайно полезной возможностью. Экспериментируйте с программами, представленными в данном уроке. И вы поймете, что динамическое распределение памяти реально выполняется очень просто.

ИСПОЛЬЗОВАНИЕ ОПЕРАТОРА new

Оператор C++ new позволяет вашим программам распределять память во время выполнения. Для использования оператораnew вам необходимо указать количество байтов памяти, которое требуется программе. Предположим, например, что вашей программе необходим 50-байтный массив. Используя операторnew, вы можете заказать эту память, как показано ниже:

char *buffer = new char[50];

Говоря кратко, если операторnew успешно выделяет память, он возвращает указатель на начало области этой памяти. В данном случае, поскольку программа распределяет память для хранения массива символов, она присваивает возвращаемый указатель переменной, определенной как указатель на типchar. Если операторnew не может выделить запрашиваемый вами объем памяти, он возвратит NULL-указатель, который содержит значение 0. Каждый раз, когда ваши программы динамически распределяют память с использованием оператораnew, они должны проверять возвращаемое операторомnew значение, чтобы определить, не равно ли оно NULL.

Зачем необходимо динамически распределять память с использованием new

Многие программы интенсивно используют массивы для хранения множества значений определенного типа. При проектировании своих программ программисты обычно пытаются объявить массивы с размерами, достаточными для удовлетворения будущих потребностей программы. К сожалению, если случится так, что потребности программы когда-нибудь превысят подобные ожидания программиста, то кому-то придется редактировать и перекомпилировать такую программу.

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

Например, следующая программа USE_NEW.CPP использует оператор new для получения указателя на 100-байтный массив:

#include

void main(void)

{
   char *pointer = new char[100];
   if (pointer != NULL) cout << "Память успешно выделена"<< endl;
   else cout << "Ошибка выделения памяти"<< endl;
}

Как видите, программа сразу проверяет значение, присвоенное операторомnew переменной-указателю. Если указатель содержит значение NULL, значитnew не смог выделить запрашиваемый объем памяти. Если же указатель содержит не NULL, следовательно, new успешно выделил память и указатель содержит адрес начала блока памяти.

Если new не может удовлетворить запрос на память, он возвратит NULL

При использовании оператораnew для выделения памяти может случиться так, что ваш запрос не может быть удовлетворен, поскольку нет достаточного объема свободной памяти. Если операторnew не способен выделить требуемую память, он присваивает указателю значение NULL. Проверяя значение указателя, как показано в предыдущей программе, вы можете определить, был ли удовлетворен запрос на память. Например, следующий оператор используетnew для распределения памяти под массив из 500 значений с плавающей точкой:

float *array = new float[100];

Чтобы определить, выделил ли операторnew память, ваша программа должна сравнить значение указателя с NULL, как показано ниже:

if (array != NULL) cout << "Память выделена успешно"<< endl;
else cout << "new не может выделить память"<< endl;

Предыдущая программа использовала операторnew для выделения 100 байт памяти. Поскольку эта программа "жестко закодирована" на объем требуемой памяти, возможно, вам потребуется ее редактировать и перекомпилировать, если возникнет необходимость, чтобы программа выделила меньше или больше памяти. Как уже кратко обсуждалось, одна из причин для динамического распределения памяти состоит в том, чтобы избавиться от необходимости редактировать и перекомпилировать программу при изменении требований к объему памяти. Следующая программа ASK_MEM.CPP запрашивает у пользователя количество байт памяти, которое необходимо выделить, и затем распределяет память, используя операторnew:

#include

void main(void)

{
   int size;
   char *pointer;
   cout << "Введите размер массива, до 30000: ";
   cin >> size;
   if (size <= 30000)

   {
      pointer = new char[size];
      if (pointer != NULL) cout << "Память выделена успешно"<< endl;
      else cout << "Невозможно выделить память"<< endl;
   }
}

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

Следующая программа NOMEMORY.CPP выделяет каждый раз память для 10000 символов до тех пор, пока операторnew не сможет больше выделить память из свободной памяти. Другими словами, эта программа удерживает выделенную память, пока не использует всю доступную свободную память. Если программа успешно выделяет память, она извещает об этом сообщением. Если память больше не может быть выделена, программа выводит сообщение об ошибке и завершается:

#include

void main(void)

{
   char * pointer;
   do

   {
      pointer = new char[10000];
      if (pointer != NULL) cout << "Выделено 10000 байт"<< endl;
      else cout << "Больше нет памяти"<< endl;
   } while (pointer 1= NULL);
}

Замечание: Если выработаете в среде MS-DOS, то, возможно, будете удивлены тем, что свободная память исчерпается после того, как программа выделит 64 Кбайт, Большинство работающих в MS-DOS компиляторов C++ по умолчанию используют малую модель памяти, которая обеспечивает только 64 К6aйm свободной памяти. Аналогично, если вы используете среду MS-DOS, то наибольшая область памяти, к которой могут обратиться ваши программы, может быть ограничена 64Кбайт.

О свободной памяти

Каждый раз при запуске вашей программы компилятор C++ устанавливает отдельную область неиспользуемой памяти, которая называется свободной памятью. Используя операторnew, ваша программа может выделить память из этой свободной памяти во время выполнения. Используя свободную память для распределения требуемой памяти, ваши программы не стеснены фиксированными размерами массивов. Размер свободной памяти может изменяться в зависимости от вашейоперационной системы и модели памяти компилятора. Если увеличивается количество динамической памяти, требуемой вашими программами, вам необходимо убедиться, что вас не сдерживают ограничения свободной памяти вашей системы.

ОСВОБОЖДЕНИЕ ПАМЯТИ, ЕСЛИ ОНА БОЛЬШЕ НЕ НУЖНА

Как вы знаете, оператор C++ new позволяет вашим программам выделять память динамически во время выполнения. Если вашей программе больше не нужна выделенная память, она должна ее освободить, используя операторdelete. Для освобождения памяти с использованием оператораdelete вы просто указываете этому оператору указатель на данную область памяти, как показано ниже:

delete pointer;

Следующая программа DEL_MEM.CPP использует операторdelete для освобождения выделенной с помощью оператораnew памяти:

#include  

#include

void main(void)

{
   char *pointer = new char[100];
   strcpy(pointer, "Учимся программировать на языке C++");
   cout << pointer << endl;
   delete pointer;
}

По умолчанию, если ваша программа не освобождает выделенную ей память до своего завершения, операционная система автоматически освобождает эту память после завершения программы. Однако если ваша программа использует операторdelete для освобождения памяти по мере того, как она (память) становится ненужной, то эта память вновь становится доступной для других целей (возможно, для вашей программы, которая опять будет использовать операторnew, или для операционной системы).

Второй пример

Следующая программа ALLOCARR.CPP выделяет память для хранения массива из 1000 целочисленных значений. Затем она заносит в массив значения от 1 до 1000, выводя их на экран. Потом программа освобождает эту память и распределяет память для массива из 2000 значений с плавающей точкой, занося в массив значения от 1.0 до 2000.0:

#include

void main(void)

{
   int *int_array = new int[1000];
   float *float_array;
   int i;
   if (int_array 1= NULL)

   {
      for (i = 0; i < 1000; i++) int_array[i] = i + 1;
      for (i = 0; i < 1000; i++) cout << int_array[i] << ' ';
      delete int_array;
   }
   float_array = new float[2000];
   if (float_array != NULL)

   {
      for (i = 0; i < 2000; i++) float_array[i] = (i + 1) • 1.0;
      for (i = 0; i < 2000; i++) cout << float_array[i] << ' ' ;
      delete float_array;
   }
}

Как правило, ваши программы должны освобождать память с помощью оператораdelete по мере того, как память становится программам не нужна.

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

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

    1. Способность выделять память динамически во время выполнения снимает с ваших программ зависимость от фиксированных размеров массивов.
    2. Если операторnew успешно выделяет требуемую вашей программой память, он возвращает указатель на начало области этой памяти.
    3. Если операторnew не может выделить требуемую вашей программой память, он возвращает NULL-указатель, который содержит значение 0.
    4. Каждый раз, когда ваши программы распределяют память динамически с использованием оператораnew, они должны проверять значение возвращаемого операторомnew указателя, чтобы определить, не равен ли он NULL, что указывает на невозможность выделения памяти.
    5. Используя указатель на массив, ваши программы могут обращаться к памяти, выделенной с помощью оператораnew.
    6. Операторnew выделяет память из блока неиспользуемой памяти, называемой свободной памятью.
    7. В зависимости от вашей операционной системы и модели памяти компилятора размер свободной памяти может быть различным. В среде MS-DOS свободная память может быть ограничена 64 Кбайт.
    8. Если вашей программе больше не нужна выделенная память, она должна освободить ее (вернуть в свободную память), используя для этого операторdelete.