Будет ли этот код C++ вызывать утечку памяти (новый массив литья)

Я работал над некоторым устаревшим кодом C++, который использует структуры переменной длины (TAPI), где размер структуры будет зависеть от строк переменной длины. Структуры распределяются путем приведения к массиву new таким образом:

STRUCT* pStruct = (STRUCT*)new BYTE[sizeof(STRUCT) + nPaddingSize];

Однако позже память освобождается с помощью вызова delete:

delete pStruct;

Будет ли это сочетание массива new[] и не-массива delete вызывать утечку памяти или это будет зависеть от компилятора? Может мне лучше изменить этот код, чтобы вместо этого использовать malloc и free?

Уже задавался очень похожий вопрос. См. <stackoverflow.com/questions/70880/…>

ChrisN 16.09.2008 18:55
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
10
1
6 928
24
Перейти к ответу Данный вопрос помечен как решенный

Ответы 24

Да, это возможно, поскольку вы выделяете с помощью new [], но освобождаете с помощью delelte, да, здесь безопаснее malloc / free, но в С ++ вы не должны их использовать, поскольку они не будут обрабатывать (де) конструкторы.

Также ваш код вызовет деконструктор, но не конструктор. Для некоторых структур это может вызвать утечку памяти (если конструктор выделил дополнительную память, например, для строки)

Лучше было бы сделать это правильно, так как это также будет правильно вызывать любые конструкторы и деконструкторы.

STRUCT* pStruct = new STRUCT;
...
delete pStruct;

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

Aaron 17.09.2008 04:10
Ответ принят как подходящий

Технически я считаю, что это может вызвать проблему с несовпадающими распределителями, хотя на практике я не знаю ни одного компилятора, который бы поступил неправильно с этим примером.

Что еще более важно, если STRUCT должен иметь (или когда-либо быть задан) деструктор, то он вызовет деструктор, не вызывая соответствующий конструктор.

Конечно, если вы знаете, откуда взялся pStruct, почему бы просто не применить его для удаления, чтобы оно соответствовало распределению:

delete [] (BYTE*) pStruct;

В этом случае вам нужно явно вызвать деструктор STRUCT перед удалением, как в ответе Ричарда Кордена.

Patrick Johnmeyer 16.09.2008 21:12

Я не понимаю, как фраза «Я не знаю ни одного компилятора, который бы делал неправильные вещи» может быть точным в свете того, что мы знаем о Visual C++ благодаря Раймонду Чену и другим источникам (см. Ссылку в ответе Лена Холгейта) .

Adam Mitz 17.09.2008 04:42

Вы могли бы вернуть BYTE * и удалить:

delete[] (BYTE*)pStruct;

Поведение кода не определено. Возможно, вам повезет (или нет), и он может работать с вашим компилятором, но на самом деле это неправильный код. С этим есть две проблемы:

  1. delete должен быть массивом delete [].
  2. delete должен вызываться указателем на тот же тип, что и назначенный тип.

Итак, чтобы быть полностью правильным, вы хотите сделать что-то вроде этого:

delete [] (BYTE*)(pStruct);

Роб Уокер Ответить хорош.

Просто небольшое дополнение, если у вас нет конструктора и / или диструкторов, поэтому вам в основном нужно выделить и освободить кусок необработанной памяти, рассмотрите возможность использования пары free / malloc.

Примечание: добавление malloc / free к базе кода открывает бесчисленные новые возможности для несоответствия распределения.

Aaron 17.09.2008 04:14

Я не хочу начинать спорить, но не могли бы вы привести пример того, как malloc / free вызовет несоответствие распределителей или какую-то ссылку.

Serge 17.09.2008 11:34

Всегда лучше, чтобы получение / выпуск любого ресурса было как можно более сбалансированным. Протекает она или нет, в данном случае сказать сложно. Это зависит от реализации компилятором распределения (де) вектора.

BYTE * pBytes = new BYTE [sizeof(STRUCT) + nPaddingSize];

STRUCT* pStruct = reinterpret_cast< STRUCT* > ( pBytes ) ;

 // do stuff with pStruct

delete [] pBytes ;

Вы как бы смешиваете способы работы C и C++. Зачем выделять больше, чем размер СТРУКТУРА? Почему не просто «новый СТРУКТ»? Если вы должны это сделать, в этом случае может быть более понятным использование malloc и free, поскольку тогда вы или другие программисты с меньшей вероятностью сделаете предположения о типах и размерах выделенных объектов.

@Reed: потому что это может быть структура переменной длины или структура, которая, по соглашению, имеет некоторое количество буферного пространства после нее для определенных целей. Например, структура данных, содержащая строки, может помещать эти строки в одну и ту же кучу. новый БАЙТ [sizeof (STRUCT) + stringSpace].

Aaron 17.09.2008 04:18

Да, это вызовет утечку памяти.

See this except from C++ Gotchas: http://www.informit.com/articles/article.aspx?p=30642 почему.

Raymond Chen has an explanation of how vector new and delete differ from the scalar versions under the covers for the Microsoft compiler... Here: http://blogs.msdn.com/oldnewthing/archive/2004/02/03/66660.aspx

IMHO, вы должны исправить удаление:

delete [] pStruct;

вместо перехода на malloc / free, хотя бы потому, что это проще сделать без ошибок;)

И, конечно же, более простое изменение, которое я показываю выше, неверно из-за приведения в исходное распределение, оно должно быть

delete [] reinterpret_cast<BYTE *>(pStruct);

так что, наверное, все-таки легко перейти на malloc / free;)

Len: проблема в том, что pStruct - это STRUCT *, но выделенная память - это BYTE [] неизвестного размера. Таким образом, delete [] pStruct не будет освобождать всю выделенную память.

Используйте оператор new и delete:

struct STRUCT
{
  void *operator new (size_t)
  {
    return new char [sizeof(STRUCT) + nPaddingSize];
  }

  void operator delete (void *memory)
  {
    delete [] reinterpret_cast <char *> (memory);
  }
};

void main()
{
  STRUCT *s = new STRUCT;
  delete s;
}

Примечание. NPaddingSize может быть динамическим, и в этом случае оператор new должен принимать два параметра (size_t classSize, size_t padding). Также: return new char [classSize + padding] s = new (nPaddingSize) STRUCT Примечание: если предоставляется один распределитель, укажите все 8 «разновидностей» по умолчанию. сделайте приватным, если не хотите.

Aaron 17.09.2008 04:23

В стандарте C++ четко сказано:

delete-expression:
             ::opt delete cast-expression
             ::opt delete [ ] cast-expression

The first alternative is for non-array objects, and the second is for arrays. The operand shall have a pointer type, or a class type having a single conversion function (12.3.2) to a pointer type. The result has type void.

In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object [...] If not, the behavior is undefined.

Значение операнда в delete pStruct является указателем на массив char, независимо от его статического типа (STRUCT*). Следовательно, любое обсуждение утечек памяти совершенно бессмысленно, потому что код плохо сформирован, и в этом случае компилятор C++ не требуется для создания разумного исполняемого файла.

Это могло привести к утечке памяти, но не могло или могло сделать что угодно, вплоть до сбоя вашей системы. Действительно, реализация C++, с которой я тестировал ваш код, прерывает выполнение программы в точке выражения удаления.

Я лично считаю, что вам лучше использовать std::vector для управления памятью, поэтому delete вам не понадобится.

std::vector<BYTE> backing(sizeof(STRUCT) + nPaddingSize);
STRUCT* pStruct = (STRUCT*)(&backing[0]);

Как только резервное копирование покидает область действия, ваш pStruct больше не действителен.

Или вы можете использовать:

boost::scoped_array<BYTE> backing(new BYTE[sizeof(STRUCT) + nPaddingSize]);
STRUCT* pStruct = (STRUCT*)backing.get();

Или boost::shared_array, если вам нужно переместить собственность.

Если вы В самом деле должны делать такие вещи, вам, вероятно, следует напрямую вызвать оператора new:

STRUCT* pStruct = operator new(sizeof(STRUCT) + nPaddingSize);

Я считаю, что такое название позволяет избежать вызова конструкторов / деструкторов.

Результату оператора new требуется статическое преобразование к соответствующему типу

James Hopkin 16.09.2008 20:41

Это имеет преимущество перед malloc / free: если глобальный оператор new был заменен, он будет использовать его.

James Hopkin 16.09.2008 20:42

Но это не сработает, если STRUCT переопределил оператор new, не так ли? Я бы предпочел разместить здесь новый синтаксис; он будет называть "правильное" размещение новым для каждого типа.

Patrick Johnmeyer 16.09.2008 21:17

Чтобы было ясно, при освобождении памяти все, что вам нужно сделать, это «удалить pStruct;» верно?

Greg Rogers 09.10.2008 02:12

В настоящее время я не могу голосовать, но Sledlime ответ предпочтительнее Ответ Роба Уокера, поскольку проблема не имеет ничего общего с распределителями или тем, есть ли у STRUCT деструктор.

Также обратите внимание, что пример кода не обязательно приводит к утечке памяти - это неопределенное поведение. Случиться могло все что угодно (от ничего плохого до аварии далеко-далеко).

Пример кода приводит к неопределенному поведению, простому и понятному. Ответ sledlime прямой и точный (с оговоркой, что слово «вектор» следует заменить на «массив», поскольку векторы относятся к STL).

Такие вещи довольно хорошо освещены в FAQ по C++ (разделы 16.12, 16.13 и 16.14):

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.12

Как указано в других сообщениях:

1) Вызовы new / delete выделяют память и могут вызывать конструкторы / деструкторы (C++ '03 5.3.4 / 5.3.5)

2) Смешивание версий new и delete в виде массива и без массива является неопределенным поведением. (C++ '03 5.3.5 / 4)

Глядя на источник, кажется, что кто-то выполнил поиск и замену malloc и free, и результат был приведен выше. В C++ есть прямая замена для этих функций, а именно прямой вызов функций распределения для new и delete:

STRUCT* pStruct = (STRUCT*)::operator new (sizeof(STRUCT) + nPaddingSize);
// ...
pStruct->~STRUCT ();  // Call STRUCT destructor
::operator delete (pStruct);

Если необходимо вызвать конструктор для STRUCT, вы можете рассмотреть возможность выделения памяти, а затем использовать размещение new:

BYTE * pByteData = new BYTE[sizeof(STRUCT) + nPaddingSize];
STRUCT * pStruct = new (pByteData) STRUCT ();
// ...
pStruct->~STRUCT ();
delete[] pByteData;

Думаю, утечки памяти нет.

STRUCT* pStruct = (STRUCT*)new BYTE [sizeof(STRUCT) + nPaddingSize];

Это транслируется в вызов выделения памяти в операционной системе, после чего возвращается указатель на эту память. Во время выделения памяти размер sizeof(STRUCT) и размер nPaddingSize будут известны для выполнения любых запросов выделения памяти для базовой операционной системы.

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

Видите ли, компилятор C / C++ не управляет памятью, а основная операционная система.

Я согласен, что есть более чистые методы, но OP сказал, что это устаревший код.

Короче говоря, я не вижу утечки памяти, поскольку принятый ответ предполагает, что она есть.

Вы имеете в виду удаление массива ([]), а не удаление вектора. Вектор - это std :: vector, и он заботится об удалении своих элементов.

ericmayo.myopenid.com настолько неправ, что кто-то с достаточной репутацией должен проголосовать против него.

Библиотеки времени выполнения C или C++ управляют кучей, которая предоставляется ему блоками операционной системой, как вы, Эрик, указали. Но является - обязанность разработчика указать компилятору, какие вызовы времени выполнения должны быть сделаны для освобождения памяти и, возможно, уничтожить объекты, которые там находятся. Удаление вектора (также известное как delete []) необходимо в этом случае, чтобы среда выполнения C++ оставила кучу в допустимом состоянии. Тот факт, что когда ПРОЦЕСС завершается, ОС достаточно умен, чтобы освободить базовые блоки памяти, не является чем-то, на что разработчикам следует полагаться. Это все равно, что вообще никогда не вызывать delete.

@Matt Cruikshank Вам следует обратить внимание и перечитать то, что я написал, потому что я никогда не предлагал не вызывать delete [] и просто позволить ОС очиститься. И вы ошибаетесь насчет библиотек времени выполнения C++, управляющих кучей. Если бы это было так, то C++ не был бы переносимым, как сегодня, и ОС никогда бы не очистила аварийное приложение. (признавая, что существуют специфические для ОС среды выполнения, которые делают C / C++ непереносимым). Я призываю вас найти stdlib.h в исходных текстах Linux на kernel.org. Ключевое слово new в C++ фактически обращается к тем же процедурам управления памятью, что и malloc.

Библиотеки времени выполнения C++ выполняют системные вызовы ОС, и именно ОС управляет кучей. Вы частично правы в том, что библиотеки времени выполнения указывают, когда освобождать память, однако на самом деле они не просматривают никакие таблицы кучи напрямую. Другими словами, среда выполнения, с которой вы связываетесь, не добавляет в ваше приложение код для обхода кучи для выделения или освобождения. Так обстоит дело в Windows, Linux, Solaris, AIX и т. д. Это также причина того, что вы не будете исправлять malloc ни в одном исходном коде ядра Linux и не найдете stdlib.h в исходном коде Linux. Поймите, что в этих современных операционных системах есть менеджеры виртуальной памяти, что еще больше усложняет ситуацию.

Вы когда-нибудь задумывались, почему вы можете вызвать malloc для 2 ГБ ОЗУ на блоке 1 ГБ и при этом вернуть действительный указатель памяти?

Управление памятью на процессорах x86 осуществляется в пространстве ядра с помощью трех таблиц. PAM (таблица размещения страниц), PD (каталоги страниц) и PT (таблицы страниц). Я говорю об аппаратном уровне. Одна из задач, которую выполняет менеджер памяти ОС, а не ваше приложение C++, - это выяснить, сколько физической памяти установлено на коробке во время загрузки, с помощью вызовов BIOS. ОС также обрабатывает исключения, например, когда вы пытаетесь получить доступ к памяти, ваше приложение тоже не имеет прав. (Ошибка общей защиты GPF).

Возможно, мы говорим то же самое, Мэтт, но я думаю, что вы можете немного запутать внутреннюю функциональность. Я использую компилятор C / C++ для жизни ...

@ericmayo - крики. Ну, экспериментируя с VS2005, я не могу получить честную утечку скалярного удаления в памяти, которое было сделано вектором new. Я предполагаю, что поведение компилятора здесь "undefined", это лучшая защита, которую я могу найти.

Вы должны признать, что делать то, что написано на оригинальном плакате, - это действительно паршивая практика.

If that were the case then C++ would not be portable as is today and a crashing application would never get cleaned up by the OS.

Однако эта логика на самом деле не работает. Мое утверждение состоит в том, что среда выполнения компилятора может управлять памятью в блоках памяти, которые ей возвращает ОС. Так работает большинство виртуальных машин, поэтому ваши аргументы против переносимости в данном случае не имеют особого смысла.

@Matt Cruikshank

«Ну, экспериментируя с VS2005, я не могу получить честную утечку из скалярного удаления в памяти, которое было сделано вектором new. Я полагаю, что поведение компилятора здесь« undefined », это лучшая защита, которую я могу найти».

Я не согласен с тем, что это поведение компилятора или даже проблема компилятора. Ключевое слово new компилируется и связывается, как вы отметили, с библиотеками времени выполнения. Эти библиотеки времени выполнения обрабатывают вызовы управления памятью в ОС в независимом от ОС согласованном синтаксисе, и эти библиотеки времени выполнения отвечают за согласованную работу malloc и новой работы между ОС, такими как Linux, Windows, Solaris, AIX и т. д. Это причина, по которой я упомянул аргумент переносимости; попытка доказать вам, что среда выполнения также не управляет памятью.

Операционная система управляет памятью.

Интерфейс библиотек времени выполнения с ОС. В Windows это библиотеки DLL диспетчера виртуальной памяти. Вот почему stdlib.h реализован в библиотеках GLIB-C, а не в исходном коде ядра Linux; если GLIB-C используется в других ОС, это реализация изменений malloc для выполнения правильных вызовов ОС. В VS, Borland и т. д. Вы никогда не найдете библиотек, которые поставляются с их компиляторами, которые действительно управляют памятью. Однако вы найдете определения malloc для конкретных ОС.

Поскольку у нас есть исходный код для Linux, вы можете посмотреть, как там реализован malloc. Вы увидите, что malloc фактически реализован в компиляторе GCC, который, в свою очередь, в основном выполняет два системных вызова Linux в ядре для выделения памяти. Никогда, сам malloc, на самом деле не управляет памятью!

И не бери это у меня. Прочтите исходный код ОС Linux или вы увидите, что говорят об этом K&R ... Вот ссылка в PDF на K&R на C.

http://www.oberon2005.ru/paper/kr_c.pdf

Смотрите в конце страницы 149: "Вызовы malloc и free могут происходить в любом порядке; вызовы malloc на операционную систему, чтобы при необходимости получить больше памяти. Эти процедуры иллюстрируют некоторые соображения, связанные с написанием машинно-зависимого кода относительно машинно-независимым способом, а также демонстрируют реальное применение структур, объединений и typedef ».

«Вы должны признать, что делать то, что написано на оригинальном плакате, - действительно паршивая практика».

О, я не согласен с этим. Я хотел сказать, что исходный код плаката не способствовал утечке памяти. Это все, что я говорил. Я не вмешивался в вопросы, связанные с передовой практикой. Поскольку код вызывает удаление, память освобождается.

Я согласен в вашу защиту, если исходный код постера никогда не выходил или никогда не выполнял вызов удаления, то в этом коде могла быть утечка памяти, но поскольку он заявляет, что позже он видит вызов удаления. «Позже, однако, память освобождается с помощью вызова удаления:»

Более того, мой ответ, как я сделал, был связан с комментарием OP «структуры переменной длины (TAPI), где размер структуры будет зависеть от строк переменной длины»

Этот комментарий звучал так, как будто он подвергал сомнению динамический характер распределения для выполняемого преобразования и, следовательно, задавался вопросом, не вызовет ли это утечку памяти. Я читал между строк, если хотите;).

Проблема (или, скорее, проблема «а») заключается в том, что выделение выполняется с помощью оператора new массива, а удаление выполняется с использованием удаления без массива. Это неопределенное поведение. Некоторые компиляторы (включая тот, который я использую) хранят служебную информацию для новых массивов, которые не будут освобождены при удалении без массива.

Michael Burr 30.09.2008 10:45

В дополнение к отличным ответам, приведенным выше, я также хотел бы добавить:

Если ваш код работает в Linux или если вы можете скомпилировать его в Linux, я бы предложил запустить его через Валгринд. Это отличный инструмент, среди множества полезных предупреждений, которые он выдает, он также сообщит вам, когда вы выделяете память как массив, а затем освобождаете ее как не-массив (и наоборот).

@eric - Спасибо за комментарии. Но ты продолжаешь говорить что-то, что сводит меня с ума:

Those run-time libraries handle the memory management calls to the OS in a OS independent consistent syntax and those run-time libraries are responsible for making malloc and new work consistently between OSes such as Linux, Windows, Solaris, AIX, etc....

Это неправда. Писатель компилятора обеспечивает реализацию, например, библиотек std, и они абсолютно бесплатны для реализации их способом ОС зависимый. Они могут, например, сделать один гигантский вызов malloc, а затем управлять памятью в блоке, как им заблагорассудится.

Совместимость обеспечивается потому, что API std и т. д. Одинаков, а не потому, что все библиотеки времени выполнения вращаются и вызывают одни и те же вызовы ОС.

Различные возможные варианты использования ключевых слов new и delete, похоже, создают изрядную путаницу. Создание динамических объектов в C++ всегда состоит из двух этапов: выделение необработанной памяти и создание нового объекта в выделенной области памяти. С другой стороны времени жизни объекта происходит разрушение объекта и освобождение того места в памяти, где находился объект.

Часто эти два шага выполняются одним оператором C++.

MyObject* ObjPtr = new MyObject;

//...

delete MyObject;

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

void* MemoryPtr = ::operator new( sizeof(MyObject) );
MyObject* ObjPtr = new (MemoryPtr) MyObject;

// ...

ObjPtr->~MyObject();
::operator delete( MemoryPtr );

Обратите внимание, что здесь не задействовано приведение типов, и в выделенной области памяти создается только один тип объекта. Использование чего-то вроде new char[N] для выделения необработанной памяти технически некорректно, поскольку логически объекты char создаются во вновь выделенной памяти. Я не знаю ни одной ситуации, когда это не «просто работает», но стирает различие между выделением необработанной памяти и созданием объекта, поэтому я не советую этого делать.

В этом конкретном случае нет никакой выгоды, разделив два шага delete, но вам нужно вручную управлять начальным распределением. Приведенный выше код работает в сценарии «все работает», но он приведет к утечке необработанной памяти в случае, когда конструктор MyObject выдает исключение. Хотя это может быть обнаружено и решено с помощью обработчика исключений в точке выделения, вероятно, лучше предоставить пользовательский оператор new, чтобы вся конструкция могла обрабатываться выражением размещения new.

class MyObject
{
    void* operator new( std::size_t rqsize, std::size_t padding )
    {
        return ::operator new( rqsize + padding );
    }

    // Usual (non-placement) delete
    // We need to define this as our placement operator delete
    // function happens to have one of the allowed signatures for
    // a non-placement operator delete
    void operator delete( void* p )
    {
        ::operator delete( p );
    }

    // Placement operator delete
    void operator delete( void* p, std::size_t )
    {
        ::operator delete( p );
    }
};

Здесь есть пара тонких моментов. Мы определяем размещение класса new, чтобы мы могли выделить достаточно памяти для экземпляра класса плюс некоторый определяемый пользователем отступ. Поскольку мы делаем это, нам нужно обеспечить соответствующее удаление размещения, чтобы в случае успешного выделения памяти, но построение не удалось, выделенная память автоматически освобождалась. К сожалению, подпись для нашего удаления размещения совпадает с одной из двух разрешенных подписей для удаления без размещения, поэтому нам нужно предоставить другую форму удаления без размещения, чтобы наше реальное удаление размещения обрабатывалось как удаление размещения. (Мы могли бы обойти это, добавив дополнительный фиктивный параметр как к нашему размещению new, так и к размещению delete, но это потребовало бы дополнительной работы на всех вызывающих сайтах.)

// Called in one step like so:
MyObject* ObjectPtr = new (padding) MyObject;

Используя одно новое выражение, мы теперь гарантируем, что память не будет протекать, если какая-либо часть нового выражения выбрасывается.

На другом конце жизненного цикла объекта, поскольку мы определили оператор delete (даже если бы мы этого не сделали, память для объекта в любом случае изначально была получена от глобального оператора new), следующий правильный способ уничтожить динамически созданный объект .

delete ObjectPtr;

Резюме!

  1. Смотри никаких повязок! operator new и operator delete имеют дело с необработанной памятью, размещение new может создавать объекты в необработанной памяти. Явное приведение void* к указателю на объект обычно является признаком чего-то логически неправильного, даже если оно «просто работает».

  2. Мы полностью проигнорировали new [] и delete []. Эти объекты переменного размера ни в коем случае не будут работать в массивах.

  3. Размещение new позволяет новому выражению не просачиваться, новое выражение по-прежнему оценивает указатель на объект, который необходимо уничтожить, и память, которую необходимо освободить. Использование одного типа интеллектуального указателя может помочь предотвратить другие типы утечек. С другой стороны, мы позволили простому delete быть правильным способом сделать это, поэтому большинство стандартных интеллектуальных указателей будут работать.

Другие вопросы по теме