Стандарты программирования на С++. 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
для перегруженного оператора