Чтение онлайн

на главную - закладки

Жанры

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

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

Шрифт:

 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

46. При наличии пользовательского

new
следует предоставлять все стандартные типы этого оператора

Резюме

Если класс определяет любую перегрузку оператора

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
— одну из трех. В таком случае это объявление также скроет остальные две версии:

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

Усадьба леди Анны

Ром Полина
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Усадьба леди Анны

Чужая дочь

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Чужая дочь

Светлая тьма. Советник

Шмаков Алексей Семенович
6. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Светлая тьма. Советник

Двойник Короля

Скабер Артемий
1. Двойник Короля
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Двойник Короля

Его нежеланная истинная

Кушкина Милена
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Его нежеланная истинная

Последний Паладин. Том 2

Саваровский Роман
2. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 2

Измена. Наследник для дракона

Солт Елена
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Измена. Наследник для дракона

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9

Мастер темных Арканов

Карелин Сергей Витальевич
1. Мастер темных арканов
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Мастер темных Арканов

Адвокат империи

Карелин Сергей Витальевич
1. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
фэнтези
5.75
рейтинг книги
Адвокат империи

Кодекс Охотника. Книга XXI

Винокуров Юрий
21. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XXI

Вечный. Книга II

Рокотов Алексей
2. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга II

Законы Рода. Том 3

Flow Ascold
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Наследник

Шимохин Дмитрий
1. Старицкий
Приключения:
исторические приключения
5.00
рейтинг книги
Наследник