У оператора-члена delete[]() может быть не один, а два параметра, при этом второй должен иметь тип size_t:
class Screen {
public:
// заменяет
// void operator delete[]( void* );
void operator delete[]( void*, size_t );
};
Если второй параметр присутствует, то компилятор автоматически инициализирует его значением, равным объему отведенной под массив памяти в байтах.
15.8.2. Оператор размещения new() и оператор delete()
Оператор-член new() может быть перегружен при условии, что все объявления имеют разные списки параметров. Первый параметр должен иметь тип size_t:
class Screen {
public:
void *operator new( size_t );
void *operator new( size_t, Screen * );
// ...
};
Остальные параметры инициализируются аргументами размещения, заданными при вызове new:
void func( Screen *start ) {
Screen *ps = new (start) Screen;
// ...
}
Та часть выражения, которая находится после ключевого слова new и заключена в круглые скобки, представляет аргументы размещения. В примере выше вызывается оператор new(), принимающий два параметра. Первый автоматически инициализируется значением, равным размеру класса Screen в байтах, а второй – значением аргумента размещения start.
Можно также перегружать и оператор-член delete(). Однако такой оператор никогда не вызывается из выражения delete. Перегруженный delete() неявно вызывается компилятором, если конструктор, вызванный при выполнении оператора new (это не опечатка, мы действительно имеем в виду new), возбуждает исключение. Рассмотрим использование delete() более внимательно.
Последовательность действий при вычислении выражения
Screen *ps = new ( start ) Screen;
* такова: Вызывается определенный в классе оператор new(size_t, Screen*).
* Вызывается конструктор по умолчанию класса Screen для инициализации созданного объекта.
Переменная ps инициализируется адресом нового объекта Screen.
Предположим, что оператор класса new(size_t, Screen*) выделяет память с помощью глобального new(). Как разработчик может гарантировать, что память будет освобождена, если вызванный на шаге 2 конструктор возбуждает исключение? Чтобы защитить пользовательский код от утечки памяти, следует предоставить перегруженный оператор delete(), который вызывается только в подобной ситуации.
Если в классе имеется перегруженный оператор с параметрами, типы которых соответствуют типам параметров new(), то компилятор автоматически вызывает его для освобождения памяти. Предположим, есть следующее выражение с оператором размещения new:
Screen *ps = new (start) Screen;
Если конструктор по умолчанию класса Screen возбуждает исключение, то компилятор ищет delete() в области видимости Screen. Чтобы такой оператор был найден, типы его параметров должны соответствовать типам параметров вызванного new(). Поскольку первый параметр new() всегда имеет тип size_t, а оператора delete() – void*, то первые параметры при сравнении не учитываются. Компилятор ищет в классе Screen оператор delete() следующего вида:
void operator delete( void*, Screen* );
Если такой оператор будет найден, то он вызывается для освобождения памяти в случае, когда new() возбуждает исключение. (Иначе – не вызывается.)
Разработчик класса принимает решение, предоставлять ли delete(), соответствующий некоторому new(), в зависимости от того, выделяет ли этот оператор new() память самостоятельно или пользуется уже выделенной. В первом случае delete() необходимо включить для освобождения памяти, если конструктор возбудит исключение; иначе в нем нет необходимости.
Можно также перегрузить оператор размещения new[]() и оператор delete[]() для массивов:
class Screen {
public:
void *operator new[]( size_t );
void *operator new[]( size_t, Screen* );
void operator delete[]( void*, size_t );
void operator delete[]( void*, Screen* );
// ...
};
Оператор new[]() используется в случае, когда в выражении, содержащем new для распределения массива, заданы соответствующие аргументы размещения:
void func( Screen *start ) {
// вызывается Screen::operator new[]( size_t, Screen* )
Screen *ps = new (start) Screen[10];
// ...
}
Если при работе оператора new конструктор возбуждает исключение, то автоматически вызывается соответствующий delete[]().
Упражнение 15.9
Объясните, какие из приведенных инициализаций ошибочны:
class iStack {
public:
iStack( int capacity )
: _stack( capacity ), _top( 0 ) {}