Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP.doc
Скачиваний:
8
Добавлен:
25.04.2019
Размер:
1.34 Mб
Скачать

6.4. Перегрузка операций управления памятью

С++ позволяет программисту реализовать собственные варианты управления памятью и обработки ошибок путем перегрузки операций выделения и освобождения динамической памяти. Выделение памяти в С++ выполняет функция с названием operator new (не путать с операцией new), а освобождение памяти – функция operator delete (не путать с delete). Для массивов выделение и освобождение памяти выполняют соответственно функции operator new[] и operator delete[] (также не путать с new[] и delete[]). Эти функции неявно вызываются операциями new, delete, new[], delete[] для соответствующих манипуляций с памятью. Так, операция new сначала обращается к функции operator new, получает от нее указатель на выделенный блок памяти и затем вызывает конструктор класса для инициализации объекта в этом блоке. Операция delete сначала вызывает деструктор класса для деинициализации объекта, а затем обращается к функции operator delete для освобождения памяти, которую занимал объект. Похожая ситуация характерна и для new[], delete[]37.

Программисту разрешено перегружать функции operator new, operator delete, а также operator new[] и operator delete[]; перегрузка соответствующих операций new, delete, new[], delete[] не допускается. Прототипы функций operator new, operator delete, operator new[] и operator delete[] сосредоточены в заголовочном файле new.h.

Для правильной перегрузки функции выделения динамической памяти new следует придерживаться ряда правил: 1) обеспечивать правильное возвращаемое значение; 2) корректно обрабатывать выделение блока памяти нулевого и «неправильного» размера; 3) осуществлять вызов функции обработки ошибок при нехватке памяти. Функция operator new должна иметь первый параметр типа size_t и может иметь дополнительные параметры; для них даже допустимы умалчиваемые значения.

Правильное возвращаемое значение функции operator new определить легко. В случае успеха это указатель на блок выделенной памяти; в случае неудачи исключение std::bad_alloc, которое выбрасывается стандартной функцией выделения памяти. Если в функцию передается запрос на блок памяти нулевого размера, то его можно просто принудительно «превращать» в запрос блока размером 1 байт. При неправильном размере блока обработку запроса следует просто передавать стандартной функции ::operator new.

Обработку ошибок в operator new можно организовать за счет переопределения стандартной функции обработки ошибок. Эта функция описана в файле new.h и определяется типом new_handler:

Typedef void (*new_handler) ();

Переопределить рассматриваемую функцию можно с помощью функции set_new_handler, которая также описана в new.h и имеет следующий прототип:

Extern new_handler set_new_handler( new_handler new_p );

Функции set_new_handler должен быть передан указатель типа new_handler на новую функцию-обработчик, при этом она возвратит указатель на старую функцию-обработчик.

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

// заголовочный файл

class NewHandler { // управление выделением памяти

public:

// замена обработчика ошибок

static new_handler set_new_handler(new_handler new_new_handler);

// выделение памяти

static void * operator new(size_t sz);

private:

// текущий обработчик ошибок

static new_handler current_handler;

};

// файл реализации

// обязательная инициализация статического компонента

new_handler NewHandler::current_handler;

// новый (пользовательский) обработчик ошибок

void MyNewHandlerFunc() {

... // специальная обработка пользователя

throw std::bad_alloc();

}

// пользовательская функция выделения памяти

void * NewHandler::operator new(size_t sz) {

sz = sz ? sz : 1; // проверяем размер на нуль

void * mem_block = 0;

// ставим новый обработчик ошибок и запоминаем указатель на старый

new_handler old_handler = set_new_handler(MyNewHandlerFunc);

try {

// пытаемся выделить память

mem_block = ::operator new(sz);

// специальная обработка пользователя

...

}

catch(std::bad_alloc &) { // попытка неудачна

set_new_handler(old_handler);

// восстанавливаем старый обработчик

throw; // передаем исключение старому обработчику

}

// восстанавливаем старый обработчик

set_new_handler(old_handler);

// возвращаем указатель на выделенный блок

return mem_block;

}

// замена обработчика ошибок

new_handler NewHandler::set_new_handler(new_handler new_new_handler) {

new_handler old_handler = ::set_new_handler(new_new_handler);

current_handler = new_new_handler;

return old_handler;

}

Функция обработки ошибок может быть специфицирована явно в заголовке функции operator new. Тогда устанавливать обработчик ошибок можно сразу при вызове operator new. Ниже дано модифицированное определение класса NewHandler и функции operator new, допускающее явное задание обработчика ошибок:

class NewHandler {

public:

// замена обработчика ошибок

static new_handler set_new_handler(new_handler new_new_handler);

// выделение памяти

static void * operator new(size_t sz, new_handler pnew_handler);

private:

// текущий обработчик ошибок

static new_handler current_handler;

};

...

void * NewHandler::operator new(size_t sz, new_handler pnew_handler) {

sz = sz ? sz : 1;

void * mem_block = 0;

new_handler old_handler = set_new_handler(pnew_handler);

... // то же, что и выше

}

Перегрузка функции operator new[] в целом аналогична перегрузке operator new. Аналогичны и правила перегрузки, однако имеется ряд жестких ограничений.

Для функции освобождения динамической памяти operator delete есть всего два правила перегрузки: 1) корректно «освобождать» память по нулевому указателю; 2) правильно реагировать на ситуацию, когда удаляемый объект имеет некорректный размер.

Вот обобщенный вариант переопределения функции operator delete:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]