Стандарты программирования на С++. 101 правило и рекомендация | страница 80



, где >parms — список типов дополнительных параметров (первый из которых всегда >std::size_t). То же относится и к операторам для массивов >new[] и >delete[].

Обсуждение

Обычно редко требуется обеспечить наличие пользовательских операторов >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 для перегруженного оператора