Уроки программирования на Visual C++. Управление свободной памятью в C++.

Источник: the-programmer
The_Programmer

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

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

#include <iostream.h>

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 <iostream.h>

#include <stdlib.h> // Прототип exit

#include <new.h> // Прототип 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 <iostream.h>

#include <stdlib.h>

#include <string.h>

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 <iostream.h>

#include <alloc.h>

#include <string.h>

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.  Однако, прежде чем это сделать, вы должны иметь четкое представление о свободной памяти (куче) и функциях библиотеки этапа выполнения, которые ею манипулируют.

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=29427