45. new и delete всегда должны разрабатываться вместе
45. new и delete всегда должны разрабатываться вместе
Резюме
Каждая перегрузка void* operator new(parms) в классе должна сопровождаться соответствующей перегрузкой оператора void operator delete(void* , parms), где 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 для перегруженного оператора 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
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Глава 7 Соединяя все вместе: ls
Глава 7 Соединяя все вместе: ls Команда V7 ls хорошо связывает воедино все, что мы до сих пор видели. Она использует почти все API, которые мы рассмотрели, затрагивая многие аспекты программирования Unix: выделение памяти, вспомогательные данные файлов, времена и даты, имена
Объединяем все вместе
Объединяем все вместе Вернемся снова к подсчету количества людей в комнате. Допустим, что можно считать по одному человеку за секунду. Следовательно, если в комнате находится 7 человек, то подсчет займет 7 секунд. Очевидно, что если будет n человек, то подсчет всех займет n
Метод Delete
Метод Delete Если параметр force равен false или не указан, то с помощью метода Delete будет нельзя удалить каталог с атрибутом "только для чтения" (read-only). Установка для force значения true позволит сразу удалять такие каталоги.При использовании метода Delete неважно, является ли заданный
3.4. Полная анонимность: I2P и Tor вместе
3.4. Полная анонимность: I2P и Tor вместе Наверное, вам интересно, какую сеть использую я? Мой выбор – Tor. Но не потому, что она чем-то лучше I2P, просто больше подходит для моих задач. Сеть I2P удобна, когда нужно обеспечить полную анонимность обмена данными между участниками сети,
5.3.3. Реклама вместе с Яндексом
5.3.3. Реклама вместе с Яндексом Лучше представить свой товар и лучше его продать можно в том случае, когда товар широко рекламируется. Поэтому Яндекс предлагает своим партнерам проведение совместных промоакций. Одним из вариантов таких воздействий на покупателей могут
DELETE
DELETE DELETE FROM <table name> [ WHERE <predicate> | WHERE CURRENT OF <cursor name> (*только для вложения*) ];Элементы, используемые в командах МОДИФИКАЦИИ ЭЛЕМЕНТ ОПРЕДЕЛЕНИЕ <cursor name> Имя курсора используемого в этой программе. <query> Допустимая команда SELECT. Для других элементов
R.5.3.4 Операция delete
R.5.3.4 Операция delete Операция delete уничтожает объект, созданный с помощью new.выражение-освобождения: ::opt delete выражение-приведения ::opt delete [] выражение-приведенияРезультат имеет тип void. Операндом delete должен быть указатель, который возвращает new. Эффект применения операции delete
1.3.3.4. А теперь — все вместе
1.3.3.4. А теперь — все вместе Комбинация описанных достаточно простых вещей позволяет построить окно с дыркой, имеющей изменяемые размеры.Для начала объявим несколько констант, которые нам потребуются при вычислении размеров дырки и т. п. (листинг 1.51).Листинг 1.51. Константы
4.9. Операторы new и delete
4.9. Операторы new и delete Каждая программа во время работы получает определенное количество памяти, которую можно использовать. Такое выделение памяти под объекты во время выполнения называется динамическим, а сама память выделяется из хипа (heap). (Мы уже касались вопроса о
15.8.1. Операторы new[ ] и delete [ ]
15.8.1. Операторы new[ ] и delete [ ] Оператор new(), определенный в предыдущем подразделе, вызывается только при выделении памяти для единичного объекта. Так, в данной инструкции вызывается new() класса Screen:// вызывается Screen::operator new()Screen *ps = new Screen( 24, 80 );тогда как ниже вызывается
Собираем все вместе
Собираем все вместе После введения в базовые механизмы ОО-вычислений настало время ответить на вопрос, каким образом можно построить исполняемую систему на основе отдельных
Усадите команду вместе
Усадите команду вместе Когда приходит время расставить столы и рассадить команду, есть одно правило, которое сложно переоценить.Усадите команду вместе!Чуть поясню, что я имею в виду:Усадите команду вместе!Людям не нравится переезжать. По крайней мере, в тех компаниях, в