Как мне написать пользовательские операторы new и delete, соответствующие стандарту ISO C++?

Как мне написать пользовательские операторы new и delete, соответствующие стандарту ISO C++?

Это продолжение раздела Перезагрузка new и delete в весьма информативном FAQ по C++, Перегрузка оператора и ее последующие действия, Почему нужно заменять операторы new и delete по умолчанию?

Раздел 1. Написание совместимого со стандартом оператора new

Раздел 2. Написание совместимого со стандартом оператора delete

-

Реализация пользовательского удалить оператор

_(Note: This is meant to be an entry to [Stack Overflow's C++ FAQ](https://stackoverflow.com/questions/tagged/c++-faq). If you want to critique the idea of providing an FAQ in this form, then [the posting on meta that started all this](https://meta.stackexchange.com/questions/68647/setting-up-a-faq-for-the-c-tag) would be the place to do that. Answers to that question are monitored in the [C++ chatroom](https://chat.stackoverflow.com/rooms/10/c-lounge), where the FAQ idea started out in the first place, so your answer is very likely to get read by those who came up with the idea.)_ *Note: The answer is based on learnings from Scott Meyers' More Effective C++ and the ISO C++ Standard.*

person Alok Save    schedule 25.08.2011    source источник
comment
вау, люди рано получают свои отрицательные голоса! - Я полагаю, вы еще даже не закончили задавать свой вопрос? Я думаю, что это хорошее место для обсуждения таких вопросов, +1 от меня.   -  person Tom    schedule 25.08.2011
comment
@Tom ОП закончен, так как сейчас он занят написанием длинного ответа. Небольшой текст утверждается на основе знаний из книги Скотта Мейера «Более эффективный C++» (обратите внимание на две орфографические опечатки, включая имя Скотта Мейерса).   -  person Sjoerd    schedule 25.08.2011
comment
@Als Похоже, есть люди, которым ты не очень нравишься :-) Лично мне не нравятся бессвязные ответы, подобные этому, я чувствую, что он должен быть где-то в специальном разделе часто задаваемых вопросов, а не затеряться среди тысяч вопросов. которые публикуются в SO каждый день. Но +1 за старание.   -  person Praetorian    schedule 25.08.2011
comment
@Praetorian: это продолжение FAQ. У нас есть отличный FAQ по перегрузке операторов, но эта тема настолько обширна, что заслуживает отдельной записи.   -  person Alok Save    schedule 25.08.2011
comment
@Als Я имел в виду не тег часто задаваемых вопросов, а специальный раздел веб-сайта, вроде FAQ по Parashift C++. Но так как у нас этого нет, это следующая лучшая вещь.   -  person Praetorian    schedule 25.08.2011
comment
@Praetorian: есть своего рода специальный раздел: c++-faq, который связан с Страница о теге c++.   -  person R. Martinho Fernandes    schedule 25.08.2011
comment
Этот вопрос действительно часто задают?   -  person James McNellis    schedule 25.08.2011
comment
Я думаю, что часто задаваемые вопросы могут также включать ответы, которые полезнее знать, чем вы когда-либо думали, когда вы часто выполняли связанную работу   -  person Lightness Races in Orbit    schedule 25.08.2011
comment
@James McNellis: У нас есть отличный FAQ от Sbi о перегрузке операторов, и он опустил этот аспект по той причине, что он требует отдельного упоминания. Это попытка сделать этот FAQ полным.   -  person Alok Save    schedule 25.08.2011
comment
Но часто ли задают этот вопрос? Если нет, то, хотя я не возражаю против того, чтобы вопрос задавался и отвечал здесь, он не должен иметь тега [c++-faq]. Тег уже слишком шумный.   -  person James McNellis    schedule 25.08.2011
comment
На самом деле я бы согласился с этим. c++-faq не для всех вопросов и ответов в книжном стиле, которые может придумать обычный пользователь.   -  person Lightness Races in Orbit    schedule 25.08.2011
comment
Хорошая статья, но, пожалуйста, расслабьтесь на типографике! Один уровень оформления за раз — и можно ли обойтись меньшим количеством обратных кавычек? Все это немного тяжело для глаз. Я рад немного отредактировать, если хотите.   -  person Kerrek SB    schedule 25.08.2011
comment
@Praetorian: Почему бы вам не перейти по ссылке на вопрос о мета, с которого все это началось, прочитать обсуждения и предложить такой раздел для SO в вопросе о мета? Или загляните в чат и обсудите это несколько дней. Если вы прочитаете обсуждения мета, вы увидите, что, когда мы начинали, мы не знали, какую форму должен принять этот вопрос с часто задаваемыми вопросами, и захват тега c++-faq был просто первым и лучшим костылем, который появился, предназначенным для временное решение, пока мы не нашли лучшее. Я был бы очень рад, если бы появились новые идеи о том, как это сделать, и их можно было бы обсудить.   -  person sbi    schedule 25.08.2011
comment
@Kerrek SB: Пожалуйста, не стесняйтесь. На самом деле, именно в одном из наших обсуждений я понял, что существующий FAQ по перегрузке операторов не распространяется на это.   -  person Alok Save    schedule 25.08.2011
comment
Есть ли какая-то причина, по которой вы не могли просто сделать это одним ответом вместо 5? Кроме того, почему это не вики сообщества?   -  person Nicol Bolas    schedule 26.08.2011
comment
@Nicol Bolas: SO ограничивает количество символов в ответе, поэтому все они не могут просто поместиться в один ответ, следовательно, отдельные ответы. Это должно было быть записью в C++-Faq для заполнения уже существующего Operator Overloading Faq. человек (я), который сформулировал ответ для FAQ. Честно говоря, большинство пользователей проголосовали за него, даже не удосужившись прочитать его. Учитывая, что это получило 10 отрицательных голосов (без причин?), я понимаю, что сообществу это не нравится, и в некотором смысле я сожалею о том, что приложил усилия, чтобы придумать это.   -  person Alok Save    schedule 26.08.2011
comment
@Als: цель вики сообщества – это больше, чем просто то, кто пишет отвечать. Это также касается того, что больше людей могут исправлять ошибки, кто получает больше/меньше голосов представителей (т.е. никто) и т. д.   -  person Nicol Bolas    schedule 26.08.2011
comment
@Nicol Bolas: Суть в том, что никто это не читает. Оставьте в стороне оценку того, что в нем неправильно или правильно. И записи, добавленные в качестве вклада, никогда не были отмечены как вики сообщества. Вы можете как-нибудь заглянуть в C++ Lounge room, чтобы обсудить философию, лежащую в основе C++ FAQ, и людей, которым пришла в голову идея начать его в первую очередь.   -  person Alok Save    schedule 26.08.2011
comment
@Nicol: Раньше это было причиной введения CW, однако это больше не полезно. для этого, потому что каждый мог вмешаться и отредактировать, даже те, у кого очень мало репутации. ИМО, единственное реальное использование CW в настоящее время состоит в том, чтобы система щелкала переключателем на вопросы, привлекающие слишком много внимания (примерно определяемые как более дюжины ответов), так что худшие случаи присущей системе голосования слабости (голосование передается из-за популярности, а не правильности) не сеет столько хаоса.   -  person sbi    schedule 26.08.2011
comment
Также будет полезен комментарий об операторе new и операторе new[] и операторе nothrow-new.   -  person Lstor    schedule 28.12.2011


Ответы (4)


Часть I

В этой записи часто задаваемых вопросов по C++ объясняется почему< /em> можно перегрузить операторы new и delete для собственного класса. Этот настоящий FAQ пытается объяснить, как это сделать в соответствии со стандартом.

Реализация пользовательского оператора new

Стандарт C++ (§18.4.1.1) определяет operator new как:

void* operator new (std::size_t size) throw (std::bad_alloc);

Стандарт C++ определяет семантику, которой должны подчиняться пользовательские версии этих операторов в §3.7.3 и §18.4.1.

Резюмируем требования.

Требование 1. Он должен динамически выделять не менее size байт памяти и возвращать указатель на выделенную память. Цитата из стандарта С++, раздел 3.7.4.1.3:

Функция выделения пытается выделить запрошенный объем памяти. В случае успеха он должен вернуть адрес начала блока памяти, длина которого в байтах должна быть не меньше запрошенного размера...

Стандарт также требует:

... Возвращенный указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в указатель любого полного типа объекта, а затем использовать для доступа к объекту или массиву в выделенном хранилище (до тех пор, пока хранилище не будет явно освобождено вызовом соответствующего функция освобождения). Даже если размер запрошенного пространства равен нулю, запрос может завершиться ошибкой. Если запрос выполнен успешно, возвращаемое значение должно быть ненулевым значением указателя (4.10) p0, отличным от любого ранее возвращенного значения p1, если только это значение p1 не было впоследствии передано оператору delete.

Это дает нам дополнительные важные требования:

Требование 2. Используемая нами функция выделения памяти (обычно malloc() или какой-либо другой пользовательский распределитель) должна возвращать соответствующим образом выровненный указатель на выделенную память, который можно преобразовать в указатель полного типа объекта и используется для доступа к объекту.

Требование 3. Наш пользовательский оператор new должен возвращать допустимый указатель, даже если запрашиваются нулевые байты.

Одно из очевидных требований, которое можно вывести даже из прототипа new:

Требование №4. Если new не может выделить динамическую память запрошенного размера, то должно быть выдано исключение типа std::bad_alloc.

Но! Это нечто большее, чем кажется на первый взгляд: если вы внимательно посмотрите на оператор new документация (цитата из стандарта следует ниже), в нем говорится:

Если для определения < strong>new_handler, эта new_handler функция вызывается стандартное определение по умолчанию для operator new, если он не может выделить запрошенное хранилище самостоятельно.

Чтобы понять, как наш пользовательский new должен поддерживать это требование, мы должны понять:

Что такое new_handler и set_new_handler?

new_handler — это typedef для указателя на функцию, которая ничего не принимает и не возвращает, а set_new_handler — это функция, которая принимает и возвращает new_handler.

Параметр set_new_handler является указателем на функцию, которую оператор new должен вызывать, если не может выделить запрошенную память. Его возвращаемое значение является указателем на ранее зарегистрированную функцию-обработчик или null, если предыдущего обработчика не было.

Подходящий момент для примера кода, чтобы прояснить ситуацию:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}

В приведенном выше примере operator new (скорее всего) не сможет выделить место для 100 000 000 целых чисел, и будет вызвана функция outOfMemHandler(), и программа прервется через выдает сообщение об ошибке.

Здесь важно отметить, что когда operator new не может выполнить запрос памяти, он многократно вызывает функцию new-handler до тех пор, пока не найдется достаточно памяти или пока не закончатся новые обработчики. В приведенном выше примере, если мы не вызовем std::abort(), outOfMemHandler() будет вызываться повторно. Следовательно, обработчик должен либо убедиться, что следующее выделение прошло успешно, либо зарегистрировать другой обработчик, либо не зарегистрировать обработчик, либо не возвращаться (т. е. завершать программу). Если нового обработчика нет и выделение не удалось, оператор выдаст исключение.

Продолжение 1


person Alok Save    schedule 25.08.2011
comment
Лично я бы сохранил результат std::set_new_handler. Тогда моя версия нового обработчика вызовет старую версию if my version failed to provide any emergency space. Таким образом, если другая библиотека установила новый обработчик, который будет вызываться этой библиотекой, как ожидается. - person Martin York; 25.08.2011
comment
Вы уверены, что new находится в namespace std? - person Kerrek SB; 25.08.2011

Часть II

.. . продолжение

Учитывая поведение operator new из примера, хорошо спроектированный new_handler должен выполнять одно из следующих действий:

Выделите больше памяти: это может привести к успешной следующей попытке выделения памяти внутри цикла оператора new. Один из способов реализовать это — выделить большой блок памяти при запуске программы, а затем освободить его для использования в программе при первом вызове обработчика new.

Установить другой новый обработчик: если текущий новый обработчик не может сделать больше доступной памяти и есть другой новый обработчик, который может, то текущий новый обработчик может установить другой new-handler на своем месте (вызвав set_new_handler). В следующий раз, когда оператор new вызовет функцию new-handler, она получит последнюю установленную.

(Разновидность этой темы заключается в том, что новый обработчик изменяет свое собственное поведение, поэтому при следующем вызове он делает что-то другое. Один из способов добиться этого — заставить новый обработчик изменить статические, специфичные для пространства имен или глобальные данные, влияющие на поведение нового обработчика.)

Удалить новый обработчик: это делается путем передачи нулевого указателя на set_new_handler. Если новый обработчик не установлен, operator new вызовет исключение ((преобразуется в) std::bad_alloc), если выделение памяти не удалось.

Вызвать исключение, конвертируемое в std::bad_alloc. Такие исключения не перехватываются operator new, но передаются на сайт, инициирующий запрос на память.

Не возвращать: по телефону abort или exit.

Чтобы реализовать специфичный для класса new_handler, мы должны предоставить классу свои собственные версии set_new_handler и operator new. set_new_handler класса позволяет клиентам указывать новый обработчик для класса (точно так же, как стандартный set_new_handlerпозволяет клиентам указывать глобальный новый обработчик). operator new класса гарантирует, что специфичный для класса новый обработчик используется вместо глобального нового обработчика при выделении памяти для объектов класса.


Теперь, когда мы лучше понимаем new_handler и set_new_handler, мы можем соответствующим образом изменить Требование №4 следующим образом:

Требование № 4 (расширенное).
Наш operator new должен попытаться выделить память более одного раза, вызывая функцию обработки новых после каждой ошибки. Здесь предполагается, что функция обработки новых данных может что-то сделать, чтобы освободить часть памяти. Только когда указатель на новую функцию обработки равен null, operator new выдает исключение.

Как и было обещано, цитата из Стандарта:
Раздел 3.7.4.1.3:

Функция распределения, которая не может выделить хранилище, может вызвать установленный в данный момент new_handler(18.4.2.2), если он есть. [Примечание: функция выделения, предоставляемая программой, может получить адрес текущего установленного new_handler с помощью функции set_new_handler (18.4.2.3).] Если функция выделения, объявленная с пустой спецификацией исключения (15.4), throw(), не может выделить память, она должен вернуть нулевой указатель. Любая другая функция распределения, которая не может выделить хранилище, должна указывать на сбой только путем создания исключения класса std::bad_alloc (18.4.2.1) или класса, производного от std::bad_alloc.

Вооружившись требованиями #4, давайте попробуем написать псевдокод для нашего new operator:

void * operator new(std::size_t size) throw(std::bad_alloc)
{  
   // custom operator new might take additional params(3.7.3.1.1)

    using namespace std;                 
    if (size == 0)                     // handle 0-byte requests
    {                     
        size = 1;                      // by treating them as
    }                                  // 1-byte requests

    while (true) 
    {
        //attempt to allocate size bytes;

        //if (the allocation was successful)

        //return (a pointer to the memory);

        //allocation was unsuccessful; find out what the current new-handling function is (see below)
        new_handler globalHandler = set_new_handler(0);

        set_new_handler(globalHandler);


        if (globalHandler)             //If new_hander is registered call it
             (*globalHandler)();
        else 
             throw std::bad_alloc();   //No handler is registered throw an exception

    }

}

Продолжение 2

person Alok Save    schedule 25.08.2011
comment
Я не понимаю, какое отношение Deinstall the new-handler имеет к ответу на вопрос «Как мне написать новый оператор C++». - person Sjoerd; 25.08.2011
comment
@Sjoerd: вам нужно будет прочитать ответ, чтобы узнать, как он организован. - person Alok Save; 25.08.2011
comment
Пожалуйста, процитируйте, где в стандарте указано, что новый оператор, указанный пользователем, должен вызывать новый обработчик. Если это не требование нового пользовательского оператора, что он делает в этом (слишком длинном) ответе?! - person Sjoerd; 25.08.2011
comment
@Sjoerd: Все цитаты из Стандарта присутствуют там, вам нужно будет прочитать, чтобы понять это. - person Alok Save; 25.08.2011
comment
Ваши ссылки относятся к стандарту С++ 98, а не к текущему стандарту С++ 11. - person Sjoerd; 25.08.2011
comment
@Sjoerd: на момент написания этой статьи текущим стандартом по-прежнему является C++03. Но если вам нужен вариант из утвержденного проекта C++11, номер абзаца такой же. - person R. Martinho Fernandes; 25.08.2011
comment
Цитата: функция распределения, которая не может выделить память, может вызвать установленную в настоящее время функцию нового обработчика (18.6.2.3), если таковая имеется. обратите внимание на CAN. Не требование. - person Sjoerd; 25.08.2011
comment
@Sjoerd: C++11 еще не является стандартом, по крайней мере, официально. Таким образом, официальным стандартом на данный момент по-прежнему является C++03. Я не против добавить соответствующие цитаты C++11 по мере их отслеживания. - person Alok Save; 25.08.2011
comment
@Sjoerd: Наш новый оператор должен попытаться выделить память более одного раза (...). Также обратите внимание на СЛЕДУЕТ. Не требование. - person R. Martinho Fernandes; 25.08.2011
comment
Новый стандарт утвержден, он будет опубликован через пару недель. Вы действительно хотите переписать все ссылки в этом посте через две недели? Источник: herbsutter.com /2011/08/12/ - person Sjoerd; 25.08.2011
comment
@Sjoerd: FDIS был одобрен. Это не стандарт, пока он не опубликован. Когда Херб говорит, что теперь это C++11, он лжет. Все, что у нас есть, — это C++0x FDIS, который по содержанию идентичен тому, что станет стандартом C++11 через несколько недель. - person Lightness Races in Orbit; 25.08.2011
comment
И моя точка зрения остается неизменной: вызов нового обработчика не является требованием для новой функции оператора, предоставляемой пользователем, поэтому он не должен быть частью ответа на вопрос. Просто перечислите 4 требования первой части ответа и максимально короткую соответствующую реализацию. Максимум пол страницы. - person Sjoerd; 25.08.2011
comment
@Sjoerd: Процитируйте, где в ответе указано, что новый оператор, предоставленный пользователем, должен вызывать новый обработчик. Если это не написано в ответе, что он делает в ваших (слишком длинных) жалобах на комментарии ?! - person sbi; 25.08.2011
comment
@sbi Вопрос касается пользовательского стандартного совместимого оператора new. Если new_handler не надо вызывать, то зачем он вообще в ответе? Это только отвлекает. - person Sjoerd; 25.08.2011
comment
@sbi Я не понимаю, почему вы ДОЛЖНЫ вызывать new_handler. конечно, МОЖНО вызвать его, но можно также вызвать множество других функций. И этот аргумент остался прежним с момента моего первого комментария, так что я не вижу, куда я переместил стойку ворот. Аргумент Строумена, у вас не было ничего лучше? Ответ слишком длинный, чтобы быть полезным, и я искал способы сократить его, чтобы сделать его более полезным. Но я уже достаточно раз высказывал здесь свое мнение, а вы все равно не понимаете моей точки зрения, так что продолжать бессмысленно. Кстати, называть меня троллем оскорбительно и грубо (и помечено как таковое). - person Sjoerd; 26.08.2011
comment
@Sjoerd: Ваши вопросы варьировались от того, почему разговор об удалении нового обработчика с помощью предоставленного пользователем нового оператора не должен вызывать новый обработчик, для которого является текущим стандартом C++. Алс и несколько очевидцев показали, что все ваши аргументы в них ложны. Теперь вы возвращаетесь с тем, что не понимаете, почему новый обработчик должен вызываться — на что вы раньше жаловались. - person sbi; 26.08.2011
comment
Тем не менее, я согласен с вашим вопросом в том смысле, что Элс мог бы пояснить, почему, по его мнению, определяемый пользователем новый оператор должен возвращаться к новому обработчику. Если бы вы спросили об этом сразу и вежливо, мы бы обсудили это, а не препирались с вами, и были бы уже намного дальше. - person sbi; 26.08.2011
comment
Что касается длины ответа: почему бы вам не предложить, как разбить его на более мелкие, независимые части? Это действительно может быть полезнее. - person sbi; 26.08.2011

Часть 3

... продолжение

Обратите внимание, что мы не можем получить новый указатель функции-обработчика напрямую, мы должны вызвать set_new_handler, чтобы узнать, что это такое. Это грубо, но эффективно, по крайней мере, для однопоточного кода. В многопоточной среде, вероятно, потребуется какая-то блокировка для безопасного манипулирования (глобальными) структурами данных, стоящими за функцией обработки новых. (Дополнительные цитаты/подробности приветствуются.)

Кроме того, у нас есть бесконечный цикл, и единственный выход из него — это успешное выделение памяти или выполнение новой функцией обработки одной из вещей, которые мы вывели ранее. Если new_handler не сделает одну из этих вещей, этот цикл внутри оператора new никогда не завершится.

Предупреждение: обратите внимание, что в стандарте (§3.7.4.1.3, процитированном выше) прямо не говорится, что перегруженный оператор new должен реализовывать бесконечный цикл, а просто говорится, что таково поведение по умолчанию. Таким образом, эта деталь открыта для интерпретации, но большинство компиляторов (GCC и Microsoft Visual C++) реализуют эту функцию цикла (вы можете скомпилировать приведенные ранее примеры кода). Также , поскольку автор C++, такой как Скотт Мейерс, предлагает этот подход, он достаточно разумен.

Специальные сценарии

Рассмотрим следующий сценарий.

class Base
{
    public:
        static void * operator new(std::size_t size) throw(std::bad_alloc);
};

class Derived: public Base
{
   //Derived doesn't declare operator new
};

int main()
{
    // This calls Base::operator new!
    Derived *p = new Derived;

    return 0;
}

Как объясняется в этом FAQ , распространенной причиной написания пользовательского диспетчера памяти является оптимизация выделения для объектов определенного класса, а не для класса или любого из его производных классов, что в основном означает, что наш новый оператор для базового класса обычно настраивается для объектов размера sizeof(Base) -ни больше, ни меньше.

В приведенном выше примере из-за наследования производный класс Derived наследует новый оператор базового класса. Это делает возможным вызов оператора new в базовом классе для выделения памяти для объекта производного класса. Лучший способ для нашего operator new справиться с этой ситуацией — перенаправить такие вызовы, запрашивающие неправильный объем памяти, на стандартный оператор new, например:

void * Base::operator new(std::size_t size) throw(std::bad_alloc)
{
    if (size != sizeof(Base))          // If size is "wrong,", that is, != sizeof Base class
    {
         return ::operator new(size);  // Let std::new handle this request
    }
    else
    {
         //Our implementation
    }
}

Обратите внимание, что проверка размера также включает наше требование №3. Это связано с тем, что все автономные объекты имеют ненулевой размер в C++, поэтому sizeof(Base) никогда не может быть нулевым, поэтому, если размер равен нулю, запрос будет переадресован на ::operator new, и гарантируется, что он обработает его стандартным образом.

Цитирование: от самого создателя C++, доктора Бьярна Страуструпа.

person Alok Save    schedule 25.08.2011

Реализация пользовательского оператора удаления

Библиотека C++ Standard(§18.4.1.1) определяет operator delete как:

void operator delete(void*) throw();

Давайте повторим упражнение по сбору требований для написания нашего пользовательского operator delete:

Требование №1: он должен возвращать void, а его первый параметр должен быть void*. Пользовательский delete operator также может иметь более одного параметра, но нам нужен только один параметр для передачи указателя, указывающего на выделенную память.

Цитата из стандарта С++:

Раздел §3.7.3.2.2:

«Каждая функция освобождения должна возвращать значение void, а ее первый параметр должен иметь значение void*. Функция освобождения может иметь более одного параметра…»

Требование № 2: должно гарантировать безопасное удаление нулевого указателя, переданного в качестве аргумента.

Цитата из стандарта C++: Раздел §3.7.3.2.3:

Значение первого аргумента, предоставленного одной из функций освобождения, предусмотренных в стандартной библиотеке, может быть нулевым значением указателя; если это так, вызов функции освобождения не имеет никакого эффекта. В противном случае значение, предоставленное operator delete(void*) в стандартной библиотеке, должно быть одним из значений, возвращенных предыдущим вызовом либо operator new(size_t), либо operator new(size_t, const std::nothrow_t&) в стандартной библиотеке, а значение, предоставленное operator delete[](void*) в стандартной библиотеке, должно быть одним из значений, возвращенных предыдущий вызов operator new[](size_t) или operator new[](size_t, const std::nothrow_t&) в стандартной библиотеке.

Требование №3: если передаваемый указатель не null, то delete operator должен освободить динамическую память, выделенную и назначенную указателю.

Цитата из стандарта C++: Раздел §3.7.3.2.4:

Если аргумент, переданный функции освобождения в стандартной библиотеке, является указателем, который не является нулевым значением указателя (4.10), функция освобождения должна освободить память, на которую ссылается указатель, делая недействительными все указатели, ссылающиеся на любую часть памяти. освобожденное хранилище.

Требование №4. Кроме того, поскольку наш специфичный для класса оператор new перенаправляет запросы «неправильного» размера на ::operator new, мы ДОЛЖНЫ перенаправлять запросы на удаление «неправильного размера» на ::operator delete.

Итак, на основе требований, которые мы суммировали выше, вот стандартный совместимый псевдокод для пользовательского delete operator:

class Base
{
    public:
        //Same as before
        static void * operator new(std::size_t size) throw(std::bad_alloc);
        //delete declaration
        static void operator delete(void *rawMemory, std::size_t size) throw();

        void Base::operator delete(void *rawMemory, std::size_t size) throw()
        {
            if (rawMemory == 0)
            {
                return;                            // No-Op is null pointer
            }

            if (size != sizeof(Base))
            {
                // if size is "wrong,"
                ::operator delete(rawMemory);      //Delegate to std::delete
                return;
            }
            //If we reach here means we have correct sized pointer for deallocation
            //deallocate the memory pointed to by rawMemory;

            return;
        }
};
person Alok Save    schedule 25.08.2011
comment
Я прочитал весь этот пост для освобождения памяти, на которую указывает часть rawMemory ... должен ли я использовать free и просто предположить, что по умолчанию operator new использовал malloc (или что-то еще)? - person lmat - Reinstate Monica; 13.02.2014