15.8.2. Оператор размещения new() и оператор delete()

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 ) {}

// ...

private:

int _top;

vatcor int _stack;

};

(a) iStack *ps = new iStack(20);

(b) iStack *ps2 = new const iStack(15);

(c) iStack *ps3 = new iStack[ 100 ];

Упражнение 15.10

Что происходит в следующих выражениях, содержащих new и delete?

class Exercise {

public:

Exercise();

~Exercise();

};

Exercise *pe = new Exercise[20];

delete[] ps;

Измените эти выражения так, чтобы вызывались глобальные операторы new() и delete().

Упражнение 15.11

Объясните, зачем разработчик класса должен предоставлять оператор delete().

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

Правило 52: Если вы написали оператор new с размещением, напишите и соответствующий оператор delete

Из книги автора

Правило 52: Если вы написали оператор new с размещением, напишите и соответствующий оператор delete Операторы new и delete с размещением встречаются в C++ не слишком часто, поэтому в том, что вы с ними не знакомы, нет ничего страшного. Вспомните (правила 16 и 17), что когда вы пишете такое


Оператор if

Из книги автора

Оператор if Оператор if имеет синтаксис двух видов:if выражение then блок_кода;if выражение then блок_кода else блок_кода;Если выражение возвращает значение True, то выполняется блок кода, расположенный после ключевого слова then, в противном случае выполняется или программный код,


Оператор if/else

Из книги автора

Оператор if/else В отличие от C и C++, оператор if/else в C# может работать только с булевыми выражениями, а не с произвольными значениями -1, 0. Поэтому в операторах if/else обычно используются операции C#, показанные в табл. 3.6. чтобы получить буквальные булевы значения.Таблица 3.6.


Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера

Из книги автора

Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера Контейнеры STL отличаются умом и сообразительностью. Они поддерживают итераторы для перебора как в прямом, так и в


Оператор while

Из книги автора

Оператор while Ключевое слово: whileОбщие замечания: Оператор while создает цикл, который повторяется до тех пор, пока проверяемое выражение не станет ложным, или нулем. Оператор while является циклом с предусловием, решение о прохождении цикла принимается до прохождения цикла.


1. Оператор Select – базовый оператор языка структурированных запросов

Из книги автора

1. Оператор Select – базовый оператор языка структурированных запросов Центральное место в языке структурированных запросов SQL занимает оператор Select, с помощью которого реализуется самая востребованная операция при работе с базами данных – запросы.Оператор Select


8.4.5. Оператор размещения new А

Из книги автора

8.4.5. Оператор размещения new А Существует третья форма оператора new, которая создает объект без отведения для него памяти, то есть в памяти, которая уже была выделена. Эту форму называют оператором размещения new. Программист указывает адрес области памяти, в которой


Оператор DELETE

Из книги автора

Оператор DELETE Запрос DELETE используется для удаления целых строк таблицы. SQL не дает возможности одному оператору DELETE удалять строки более чем из одной таблицы. Запрос DELETE, который изменяет только одну текущую строку курсора, называется позиционированным удалением.


1.4.7 Оператор while

Из книги автора

1.4.7 Оператор while Рассмотрим копирование строки, когда заданы указатель p на ее первый символ и указатель q на целевую строку. По соглашению строка оканчивается символом с целым значением 0.while (p != 0) (* *q = *p; // скопировать символ q = q+1; p = p+1; *) *q = 0; // завершающий символ 0 скопирован


9.6 Оператор For

Из книги автора

9.6 Оператор For Оператор for имеет видfor (оператор_1 выражение_1 opt; выражение_2 opt) оператор_2Этот оператор эквивалентен следующему:оператор_1 while ( выражение_1 ) (* оператор_2 выражение_2 ; *)за исключением того, что continue в операторе_2 будет выполнять выражение_2 перед выполнением


3.3. Оператор &

Из книги автора

3.3. Оператор & При выполнении задания в экранном режиме происходит "захват" терминала на весь этот период. Перевод задания в фоновый режим позволяет освободить терминал для других целей. Чтобы выполнить команду в фоновом режиме, укажите после нее оператор &:команда


6.1. Оператор &&

Из книги автора

6.1. Оператор && Общий формат оператора && таков:команда1 && команда2Эта инструкция обрабатывается следующим образом: правый операнд интерпретируется только тогда, когда левый операнд равен TRUE. Иными словами, вторая команда выполняется в том случае, если первая