Выбрать главу

С помощью оператора разрешения глобальной области видимости можно вызвать глобальный delete(), даже если в Screen определена собственная версия:

::delete ps;

В общем случае используемый оператор delete() должен соответствовать тому оператору new(), с помощью которого была выделена память. Например, если ps указывает на область памяти, выделенную глобальным new(), то для ее освобождения следует использовать глобальный же delete().

Оператор delete(), определенный для типа класса, может содержать два параметра вместо одного. Первый параметр по-прежнему должен иметь тип void*, а второй – предопределенный тип size_t (не забудьте включить заголовочный файл ):

class Screen {

public:

// заменяет

// void operator delete( void * );

void operator delete( void *, size_t );

};

Если второй параметр есть, компилятор автоматически инициализирует его значением, равным размеру адресованного первым параметром объекта в байтах. (Этот параметр важен в иерархии классов, когда оператор delete() может наследоваться производным классом. Подробнее наследование обсуждается в главе 17.)

Рассмотрим реализацию операторов new() и delete() в классе Screen более детально. В основе нашей стратегии распределения памяти будет лежать связанный список объектов Screen, на начало которого указывает член freeStore. При каждом обращении к оператору-члену new() возвращается следующий объект из списка. При вызове delete() объект возвращается в список. Если при создании нового объекта список, адресованный freeStore, пуст, то вызывается глобальный оператор new(), чтобы получить блок памяти, достаточный для хранения screenChunk объектов класса Screen.

Как screenChunk, так и freeStore представляют интерес только для Screen, поэтому мы сделаем их закрытыми членами. Кроме того, для всех создаваемых объектов нашего класса значения этих членов должны быть одинаковыми, а следовательно, нужно объявить их статическими. Чтобы поддержать структуру связанного списка объектов Screen, нам понадобится третий член next:

class Screen {

public:

void *operator new( size_t );

void operator delete( void *, size_t );

// ...

private:

Screen *next;

static Screen *freeStore;

static const int screenChunk;

};

Вот одна из возможных реализаций оператора new() для класса Screen:

#include "Screen.h"

#include cstddef

// статические члены инициализируются

// в исходных файлах программы, а не в заголовочных файлах

Screen *Screen::freeStore = 0;

const int Screen::screenChunk = 24;

void *Screen::operator new( size_t size )

{

Screen *p;

if ( !freeStore ) {

// связанный список пуст: получить новый блок

// вызывается глобальный оператор new

size_t chunk = screenChunk * size;

freeStore = p =

reinterpret_cast Screen* ( new char[ chunk ] );

// включить полученный блок в список

for ( ;

p != &freeStore[ screenChunk - 1 ];

++p )

p-next = p+1;

p-next = 0;

}

p = freeStore;

freeStore = freeStore-next;

return p;

}

А вот реализация оператора delete():

void Screen::operator delete( void *p, size_t )

{

// вставить "удаленный" объект назад,

// в список свободных

( static_cast Screen* ( p ) )-next = freeStore;

freeStore = static_cast Screen* ( p );

}

Оператор new() можно объявить в классе и без соответствующего delete(). В таком случае объекты освобождаются с помощью одноименного глобального оператора. Разрешается также объявить и оператор delete() без new(): объекты будут создаваться с помощью одноименного глобального оператора. Однако обычно эти операторы реализуются одновременно, как в примере выше, поскольку разработчику класса, как правило, нужны оба.

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

Выделение памяти с помощью оператора new(), например:

Screen *ptr = new Screen( 10, 20 );

эквивалентно последовательному выполнению таких инструкций: