Вход/Регистрация
Стандарты программирования на С++. 101 правило и рекомендация
вернуться

Александреску Андрей

Шрифт:
Обсуждение

Обычно редко требуется обеспечить наличие пользовательских операторов

new
или
delete
, но если все же требуется один из них — то обычно требуются они оба. Если вы определяете специфичный для данного класса оператор
T::operator new
, который выполняет некоторое специальное выделение памяти, то, вероятнее всего, вы должны определить и специфичный для данного класса оператор
T::operator delete
, который выполняет соответствующее освобождение выделенной памяти.

Появление данной рекомендации связано с одной тонкой проблемой: дело в том, что компилятор может вызвать перегруженный оператор

T::operator delete
даже если вы никогда явно его не вызываете. Вот почему вы всегда должны предоставлять операторы
new
и
delete
(а также операторы
new[]
и
delete[]
) парами.

Пусть вы определили класс с пользовательским выделением памяти:

class T {

 // ...

 static void* operator new(std::size_t);

 static void* operator new(std::size_t, CustomAllocator&);

 static void operator delete(void*, std::size_t);

};

Вы вводите простой протокол для выделения и освобождения памяти.

• Вызывающий код может выделять объекты типа

T
либо при помощи распределителя по умолчанию (используя вызов
new T
), либо при помощи пользовательского распределителя (вызов
new(allос) T
, где
allос
— объект типа
CustomAllocator
).

• Единственный оператор

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

Пока все в порядке.

Однако компилятор может скрыто вызвать другую перегрузку оператора

delete
, а именно
T::operator delete(size_t, CustomAllocator&)
. Это связано с тем, что инструкция

T* р = new(alloc) T;

на самом деле разворачивается в нечто наподобие

// Сгенерированный компилятором код для

// инструкции T* p = new(alloc)T;

//

void* __compilerTemp = T::operator new(sizeof(T), alloc);

T* p;

try {

 p = new (__compilerTemp) T; // Создание объекта T по

// адресу __compilerTemp

} catch(...) { // Сбой в конструкторе...

 T::operator delete(__compilerTemp, sizeof(T), alloc);

 throw;

}

Итак, компилятор автоматически вставляет код вызова соответствующего оператора

T::operator delete
для перегруженного оператора
T::operator new
, что совершенно логично, если выделение памяти прошло успешно, но произошел сбой в конструкторе. "Соответствующая" сигнатура оператора
delete
имеет вид
void operator delete(void*, параметры_оператора_new)
.

Теперь перейдем к самому интересному. Стандарт С++ ([C++03] §5.3.4(17)) гласит, что приведенный выше код будет генерироваться тогда и только тогда, когда реально существует соответствующая перегрузка оператора

delete
. В противном случае код вообще не будет вызывать никакого оператора
delete
при сбое в конструкторе. Другими словами, при сбоях в конструкторе мы получим утечку памяти. Из шести проверенных нами распространенных компиляторов только два выводили предупреждение в такой ситуации. Вот почему каждый перегруженный оператор
void* operator new(parms)
должен сопровождаться соответствующей перегрузкой
void operator delete(void*, parms)
.

Исключения

Размещающий оператор

new

void* T::operator new(size_t, void* p) { return p; }

не требует наличия соответствующего оператора

delete
, поскольку реального выделения памяти при этом не происходит. Все протестированные нами компиляторы не выдавали никаких предупреждений по поводу отсутствия оператора
void T::operator delete(void*, size_t, void*)
.

Ссылки

[C++03] §5.3.4 • [Stroustrup00] §6.2.6.2, §15.6 • [Sutter00] §36

  • Читать дальше
  • 1
  • ...
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: