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

  • Вы можете создать свой собственный обработчик ситуации, когда памяти недостаточно — функции, которую C++ вызывает, еслиnew не может удовлетворить запрос на память.
  • C++ позволяет вам определить собственный операторnew для выделения и, возможно, инициализации памяти.
  • C++ позволяет вам определить собственный операторdelete для освобождения памяти.

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

СОЗДАНИЕ ОБРАБОТЧИКА ДЛЯ ОПЕРАЦИЙ СО СВОБОДНОЙ ПАМЯТЬЮ

Как вы уже знаете из урока 31, если операторnew не может выделить требуемую память из свободной памяти, он присваивает значение NULL вашей переменной-указателю. Следующая программа USE_FREE.CPP неоднократно вызывает операторnew, выделяя каждый раз 1000 байт, пока свободная память не исчерпается:

#include

void main (void)

{
   char *pointer;
   do

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

Как видите, программа просто выполняет цикл, покаnew не присвоит указателю значение NULL. Если вы хотите, чтобыnew выполнил другие действия (что-нибудь отличное от тупого возвращения значения NULL), когда он не может удовлетворить запрос на память, то сначала вам следует определить функцию, которую должна вызывать ваша программа, если памяти недостаточно для удовлетворения запроса. Например, следующая функцияend_pro-gram выводит на экран сообщение, а затем использует функцию библиотеки этапа выполненияexit для завершения программы:

void end_program(void)

{
   cout << "Запрос на память не может быть удовлетворен" << endl;
   exit(l);
}

Чтобы заставить C++ вызывать функциюend_program, еслиnew не может удовлетворить запрос на память, вам необходимо вызвать функцию set_new_handler, указав ей функциюend_program в качестве параметра, как показано ниже:

set_new_handler(end_program);

Следующая программа END_FREE.CPP вызывает функциюend_program, еслиnew не может удовлетворить запрос на память:

#include

#include // Прототип exit

#include // Прототип set_new_handler

void end_program(void)

{
   cout << "Запрос на память не может быть удовлетворен" << endl;
   exit(l);
}

void main(void)

{
   char* pointer;
   set_new_handler(end_program);
   do

   {
      pointer = new char[10000];
      cout << "Выделено 10000 байт"<< endl;
   } while (1);
}

В данном случае программа просто завершается, еслиnew не может выделить память из свободной памяти. В зависимости от потребностей вашей программы вы могли бы использовать функцию для выделения памяти из другого источника, например из расширенной памяти компьютера, которая существует в среде MS-DOS. Кроме того, ваша программа могла бы освободить память распределенную ею для других целей, чтобы сделать доступной свободную память. Обеспечивая вашим программам возможность создавать обработчик ситуации отсутствия памяти, C++ предоставляет вам полный контроль над процессом распределения памяти.

СОЗДАНИЕ СОБСТВЕННЫХ ОПЕРАТОРОВ new И delete

Как вы знаете, C++ позволяет вашим программам перегружать операторы. Аналогично вы можете перегрузить операторыnew иdelete, чтобы изменить их поведение. Например, предположим, что вы выделяете 100 байт памяти для хранения супер-секретных данных о вашей компании. Когда вы в дальнейшем освобождаете эту память с помощью оператораdelete, освобождается буфер, который содержал эту память, т.е. те самые 100 байт, содержащие супер-секретные данные о вашей компании. Предположим, корпоративный шпион (и программист) имеет доступ к вашему компьютеру, его программа теоретически может распределить тот же 100-байтный массив в памяти вашего компьютера и изучить ваши супер-секреты. Перегружаяоператорdelete, ваша программа может сначала заполнить этот буфер нулями или другими бессмысленными символами, а потом освободить эту память. Следующая программа MYDELETE.CPP перегружает операторdelete. Она сначала перезаписывает 100 байт, на которые указывает указатель, а затем освобождает память, используя для этого функцию библиотеки этапа выполненияfree:

#include  

#include  

#include

static void operator delete(void *pointer)

{
   char *data = (char *) pointer;
   int i;
   for (i = 0; i < 100; i++) data[i] = 0;
   cout << "Секрет в безопасности!"<< endl;
   free(pointer);
}

void main(void)

{
   char *pointer = new char[100];
   strcpy(pointer, "Секреты моей компании");
   delete pointer;
}

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

char *data = (char *) pointer;

Символы(char *), которые называются оператором приведения типов, предназначены только для того, чтобы сообщить компилятору C++, что функция знает, что она присваивает указатель типаvoid (см. выше параметры функции) указателю типаchar. Если вы опустите оператор приведения типов, программа не откомпилируется. Затем функция копирует нули в 100 байт буфера и освобождает память, используя для этого функцию библиотеки этапа выполнения free. Очень важно отметить, что эта функция (операторdelete) работает только с областью памяти размером 100 байт. Поскольку данная программа выделяет память только один раз, она работает корректно. Если вы измените программу таким образом, чтобы выделялось только десять байт памяти и не сделаете подобных изменений в этой функции, то она перезапишет 90 байт памяти, которые ваша программа, возможно, использовала для других целей, приведя к ошибке. Однако, используя функции библиотеки этапа выполнения, ваши программы могут получить больше информации о размере области памяти, на которую указывает определенный указатель.

Подобным образом следующая программа NEW_OVER.CPP перегружает оператор C++ new. В данном случае перегруженная функция помещает символьную строку "Учимся программировать на языке C++!" в начало выделяемой памяти:

#include  

#include  

#include

static void *operator new(size_t size)

{
   char *pointer;
   pointer = (char *) malloc(size);
   if (size > strlen( "Учимся программировать на языке C++!"))
       strcpy(pointer, "Учимся программировать на языке    C++!");
   return(pointer);
}

void main(void)

{
   char *str = new char[100];
   cout << str << endl;
}

Как видите, функцияnew использует для выделения памяти функцию malloc библиотеки этапа выполнения. Если размер выделяемой памяти достаточен для хранения строки "Учимся программировать на языке C++!", данная функция использует функциюstrcpy библиотеки этапа выполнения для копирования строки в область памяти.

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

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

    1. Если операторnew не может удовлетворить запрос на память, то по умолчанию он присваивает значение NULL соответствующему указателю.
    2. Если вашим программам необходима другая обработка в том случае, когдаnew не может удовлетворить запрос на память, ваши программы могут определить свои собственные обработчики. Используя функцию set_new_handler, программа может заставитьnew вызвать вашу функцию, если невозможно удовлетворить запрос на память.
    3. C++ позволяет вашим программам перегружать операторыnew и delete. Однако, прежде чем это сделать, вы должны иметь четкое представление о свободной памяти (куче) и функциях библиотеки этапа выполнения, которые ею манипулируют.