Теперь перейдем к самому интересному. Стандарт С++ ([C++03] §5.3.4(17)) гласит, что приведенный выше код будет генерироваться тогда и только тогда, когда реально существует соответствующая перегрузка оператора
delete
. В противном случае код вообще не будет вызывать никакого оператора
delete
при сбое в конструкторе. Другими словами, при сбоях в конструкторе мы получим утечку памяти. Из шести проверенных нами распространенных компиляторов только два выводили предупреждение в такой ситуации. Вот почему каждый перегруженный оператор
, поскольку реального выделения памяти при этом не происходит. Все протестированные нами компиляторы не выдавали никаких предупреждений по поводу отсутствия оператора
следует предоставлять все стандартные типы этого оператора
Резюме
Если класс определяет любую перегрузку оператора
new
, он должен перегрузить все три стандартных типа этого оператора — обычный
new
, размещающий и не генерирующий исключений. Если этого не сделать, то эти операторы окажутся скрытыми и недоступными пользователям вашего класса.
Обсуждение
Обычно пользовательские операторы
new
и
delete
нужны очень редко, но если они все же оказываются необходимы, то вряд ли вы захотите, чтобы они скрывали встроенные сигнатуры.
В С++, после того как вы определите имя в области видимости (например, в области видимости класса), все такие же имена в охватывающих областях видимости окажутся скрыты (например, в базовых классах или охватывающих пространствах имен), так что перегрузка никогда не работает через границы областей видимости. Когда речь идет об имени оператора
new
, необходимо быть особенно осторожным и внимательным, чтобы не усложнять жизнь себе и пользователям вашего класса.
Пусть вы определили следующий оператор
new
, специфичный для класса:
class С {
// ...
// Скрывает три стандартных вида оператора new
static void* operator new(size_t, MemoryPool&);
};
Теперь, если кто-то попытается написать выражение с обычным стандартным
new
С, компилятор сообщит о том, что он не в состоянии найти обычный старый оператор
new
. Объявление перегрузки
C::operator new
с параметром типа
MemoryPool
скрывает все остальные перегрузки, включая знакомые встроенные глобальные версии, которые все мы знаем и любим:
void* operator new(std::size_t); // Обычный
void* operator new(std::size_t,
std::nothrow_t) throw; // He генерирующий исключений
void* operator new(std::size_t,
void*); // Размещающий
В качестве другого варианта событий предположим, что ваш класс предоставляет некоторую специфичную для данного класса версию оператора
new
— одну из трех. В таком случае это объявление также скроет остальные две версии: