В каких случаях использовать malloc и / или new?

Я вижу, что в C ++ существует несколько способов выделения и освобождения данных, и я понимаю, что когда вы вызываете malloc, вы должны вызывать free, а когда вы используете оператор new, вы должны соединяться с delete, и смешивать их будет ошибкой (например, Calling free() на то, что было создано с помощью оператора new), но я не понимаю, когда мне следует использовать malloc / free, а когда я должен использовать new / delete в моих реальных программах.

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


person Community    schedule 08.10.2008    source источник
comment
Я просто хотел бы добавить напоминание о том, что вы не можете смешивать два стиля, то есть вы не можете использовать new для создания объекта и затем вызывать для него free (), а также пытаться удалить блок, выделенный malloc (). Наверное, это очевидно, но тем не менее ...   -  person nsayer    schedule 08.10.2008
comment
Хорошие ответы, все, что я должен добавить (чего я не видел), это то, что new / delete вызывает для вас конструктор / деструктор, а malloc / free - нет. Просто стоит упомянуть разницу.   -  person Bill K    schedule 09.10.2008
comment
В современном C ++ я все еще пытаюсь найти причину для использования того и другого.   -  person Rahly    schedule 22.12.2017
comment
Или не используйте ни то, ни другое и используйте std: shared_ptr ‹T›. Тогда вам вообще не нужно удалять.   -  person Vincent    schedule 27.02.2018


Ответы (19)


Если вы не вынуждены использовать C, вы должны никогда не использовать malloc. Всегда используйте new.

Если вам нужен большой кусок данных, просто сделайте что-нибудь вроде:

char *pBuffer = new char[1024];

Будьте осторожны, хотя это неверно:

//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;

Вместо этого вы должны сделать это при удалении массива данных:

//This deletes all items in the array
delete[] pBuffer;

Ключевое слово new - это способ сделать это в C ++, и оно гарантирует, что у вашего типа будет вызван конструктор. Ключевое слово new также более типобезопасно, тогда как malloc вообще не типобезопасно.

Единственный способ, которым я мог подумать, что было бы полезно использовать malloc, - это если вам нужно изменить размер вашего буфера данных. У ключевого слова new нет аналогичного пути, как у realloc. Функция realloc может помочь вам более эффективно увеличить размер блока памяти.

Следует отметить, что нельзя смешивать _13 _ / _ 14_ и _15 _ / _ 16_.

Примечание. Некоторые ответы на этот вопрос неверны.

int* p_scalar = new int(5);  // Does not create 5 elements, but initializes to 5
int* p_array  = new int[5];  // Creates 5 elements
person Brian R. Bondy    schedule 08.10.2008
comment
Что касается вызова delete foo, когда вы должны вызвать delete [] foo, некоторые компиляторы исправят это автоматически, а не утечку, а другие удалят только первую запись и утечку. У меня было несколько из них в коде, и valgrind найдет их для вас. - person KPexEA; 08.10.2008
comment
Я проверил это много лет назад на VC ++ с массивом объектов и установил точку останова в свой деструктор. Удалили только первую. Но да, хороший момент, когда некоторые компиляторы поймают это за вас. - person Brian R. Bondy; 08.10.2008
comment
Я не думаю, что есть утечка памяти, если вы не используете синтаксис []. Но будет вызываться только деструктор для первого элемента массива. - person Ferruccio; 09.10.2008
comment
Если ваш компилятор не преобразует автоматически ваш delete в delete [], то определенно есть утечка памяти. - person Brian R. Bondy; 09.10.2008
comment
Если вы не используете правильное удаление , результат не определен. Это неверно. Тот факт, что он может частично исправить или сработать, - это просто слепая удача. - person Michael Burr; 09.10.2008
comment
Я не думаю, что это вызовет повреждение кучи, он просто увидит это как указатель на один элемент. - person Brian R. Bondy; 09.10.2008
comment
См. Пункты 16.12, 16.14 и 38.7 в C ++ FAQ: parashift.com/c ++ - faq-lite / freestore-mgmt.html # faq-16.12 parashift.com/c ++ - faq-lite / compiler-dependencies.html # faq-38.7 - person Michael Burr; 09.10.2008
comment
@KPexEA: Даже если некоторые компиляторы могут исправить ваши ошибки, делать их в первую очередь неправильно :) Всегда используйте delete [] там, где это необходимо. - person korona; 09.10.2008
comment
Если вы не вынуждены использовать C, вы никогда не должны использовать malloc. Всегда используйте новый. Почему? В чем здесь выигрыш? Для объектов нам нужна конструкция, но для блоков памяти вы четко документируете два способа сделать ошибки кодирования (более легко отловленный () vs [] в new и менее легко обнаруживаемый несоответствующий массив vs scaler new и delete). Какова мотивация использования new / delete для блоков необработанной памяти? - person Ben Supnik; 11.02.2010
comment
типа безопасные средства? Кто-нибудь может уточнить? - person ; 05.05.2011
comment
безопасность типов - это степень, в которой язык программирования препятствует или предотвращает ошибки типа - первая строка википедии - person Niroshan; 08.06.2011
comment
Никогда, никогда не используйте new[] и delete[], всегда используйте std::vector. - person Puppy; 21.12.2011
comment
@DeadMG: Если кто-то создает массив для использования асинхронной функцией API, разве new[] не будет намного безопаснее, чем std::vector? Если использовать new[], единственный способ, которым указатель станет недействительным, будет через явное delete, в то время как память, выделенная для std::vector, может стать недействительной, когда вектор изменит размер или покинет область видимости. (Обратите внимание, что при использовании new[] нужно было бы учесть возможность того, что нельзя будет вызвать delete, если асинхронный метод все еще ожидает выполнения; если может потребоваться отказаться от асинхронной операции, возможно, придется организовать удаление через Перезвоните). - person supercat; 18.02.2013
comment
А как насчет многопоточности, например, с OpenMP? Они одинаково безопасны для потоков? - person nullgraph; 30.01.2015
comment
А что с realloc() ?, нет renew - person Iharob Al Asimi; 23.04.2015
comment
На самом деле никогда не используйте new, но предпочитайте его перед malloc, если вам действительно нужно использовать один из них. Оба они требуют ручного управления памятью, сделанного вами. При (почти) всех обстоятельствах у вас есть std::vector и интеллектуальные указатели, которые освобождают память при их уничтожении. - person Marian Spanik; 29.02.2016
comment
realloc - плохая причина отдавать предпочтение malloc, учитывая, что почти все звонят realloc неправильно. - person Paul Coccoli; 20.11.2016
comment
еще одна плохая причина - каллок - person ejectamenta; 24.01.2017
comment
@immibis Этого недостаточно? он гарантирует, что конструктор вашего типа с именем [...] также более безопасен по типу, тогда как malloc вообще не является типобезопасным. - person IS4; 05.09.2017
comment
Почему malloc небезопасен по типу? - person scottxiao; 23.04.2018
comment
@PaulCoccoli, а альтернативой realloc для new инициализированных количеств является? - person Carpetfizz; 22.08.2018
comment
@Carpetfizz Это зависит от того, какие new инициализированные количества. Как уже неоднократно говорилось выше, не существует new-подобной версии realloc. - person Paul Coccoli; 23.08.2018
comment
Может ли это: int* i = new int{ 5 }; быть лучшим способом инициализации, чем использование круглых скобок? - person Dynamic Squid; 13.08.2020

Короткий ответ: не используйте malloc для C ++ без действительно веской причины. malloc имеет ряд недостатков при использовании с C ++, которые new были определены для устранения.

Недостатки, исправленные новым для кода C ++

  1. malloc не является типизированным в каком-либо значимом смысле. В C ++ вы должны выполнить возврат от void*. Это потенциально создает множество проблем:

    #include <stdlib.h>
    
    struct foo {
      double d[5];
    }; 
    
    int main() {
      foo *f1 = malloc(1); // error, no cast
      foo *f2 = static_cast<foo*>(malloc(sizeof(foo)));
      foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad
    }
    
  2. Хотя все еще хуже. Если рассматриваемый тип - POD (простые старые данные), то вы можете частично - разумно использовать malloc для выделения памяти для него, как это делает f2 в первом примере.

    Однако это не так очевидно, если тип - POD. Тот факт, что данный тип может измениться с POD на не-POD без результирующей ошибки компилятора и потенциально очень трудных для отладки проблем, является важным фактором. Например, если кто-то (возможно, другой программист, во время обслуживания, намного позже, внесет изменение, из-за которого foo больше не будет POD, то во время компиляции не появится очевидная ошибка, как вы надеетесь, например:

    struct foo {
      double d[5];
      virtual ~foo() { }
    };
    

    приведет к тому, что malloc из f2 также станет плохим без какой-либо очевидной диагностики. Пример здесь тривиален, но можно случайно ввести не-POD-качество намного дальше (например, в базовый класс, добавив не-POD-член). Если у вас есть C ++ 11 / boost, вы можете использовать is_pod, чтобы проверить правильность этого предположения и выдать ошибку, если это не так:

    #include <type_traits>
    #include <stdlib.h>
    
    foo *safe_foo_malloc() {
      static_assert(std::is_pod<foo>::value, "foo must be POD");
      return static_cast<foo*>(malloc(sizeof(foo)));
    }
    

    Хотя ускорение невозможно определить, типом является POD без C ++ 11 или некоторых других расширений компилятора.

  3. malloc возвращает NULL в случае сбоя выделения. new выкинет std::bad_alloc. Поведение при последующем использовании указателя NULL не определено. Исключение имеет чистую семантику, когда оно выбрасывается из источника ошибки. Обертывание malloc подходящим тестом при каждом вызове кажется утомительным и подверженным ошибкам. (Вам нужно только один раз забыть, чтобы отменить всю эту хорошую работу). Исключению может быть разрешено распространяться на уровень, на котором вызывающий может разумно его обработать, тогда как NULL гораздо сложнее передать его обратно осмысленно. Мы могли бы расширить нашу safe_foo_malloc функцию, чтобы генерировать исключение, или выйти из программы, или вызвать какой-нибудь обработчик:

    #include <type_traits>
    #include <stdlib.h>
    
    void my_malloc_failed_handler();
    
    foo *safe_foo_malloc() {
      static_assert(std::is_pod<foo>::value, "foo must be POD");
      foo *mem = static_cast<foo*>(malloc(sizeof(foo)));
      if (!mem) {
         my_malloc_failed_handler();
         // or throw ...
      }
      return mem;
    }
    
  4. По сути, malloc - это функция C, а new - функция C ++. В результате malloc не очень хорошо работает с конструкторами, он смотрит только на выделение блока байтов. Мы могли бы расширить наш safe_foo_malloc, чтобы использовать размещение new:

    #include <stdlib.h>
    #include <new>
    
    void my_malloc_failed_handler();
    
    foo *safe_foo_malloc() {
      void *mem = malloc(sizeof(foo));
      if (!mem) {
         my_malloc_failed_handler();
         // or throw ...
      }
      return new (mem)foo();
    }
    
  5. Наша функция safe_foo_malloc не очень универсальна - в идеале нам нужно что-то, что может обрабатывать любой тип, а не только foo. Мы можем добиться этого с помощью шаблонов и вариативных шаблонов для конструкторов, отличных от стандартных:

    #include <functional>
    #include <new>
    #include <stdlib.h>
    
    void my_malloc_failed_handler();
    
    template <typename T>
    struct alloc {
      template <typename ...Args>
      static T *safe_malloc(Args&&... args) {
        void *mem = malloc(sizeof(T));
        if (!mem) {
           my_malloc_failed_handler();
           // or throw ...
        }
        return new (mem)T(std::forward(args)...);
      }
    };
    

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

person Flexo    schedule 01.11.2011
comment
Жаль, что в C ++ struct и class означают в основном одно и то же; Интересно, возникли бы какие-либо проблемы с тем, чтобы struct были зарезервированы для POD и, возможно, все class типы считались не-POD. Любые типы, определенные кодом, предшествующим изобретению C ++, обязательно будут POD, поэтому я не думаю, что обратная совместимость будет здесь проблемой. Есть ли преимущества в том, что типы, не относящиеся к POD, объявлены как struct, а не как class? - person supercat; 18.02.2013
comment
@supercat Немного поздно, но, как выяснилось, заставить struct и class делать почти одно и то же было замечательным дизайнерским решением, которое теперь включает удобную функцию под названием метаклассы (от Herb). - person Rakete1111; 13.10.2018
comment
@ Rakete1111: На первый взгляд это предложение выглядит так, как будто оно предварительно обрабатывает версию языка, в которой используются ключевые слова с префиксом доллара, такие как $class. Однако я не уверен, какое отношение это имеет к class и struct, являющимся синонимами. - person supercat; 13.10.2018
comment
@supercat Система типов была бы более раздвоенной. Если class и struct означают одно и то же, вы можете выполнять произвольные преобразования над ними ($class), не беспокоясь о том, чтобы сделать class a struct и наоборот. - person Rakete1111; 13.10.2018
comment
@ Rakete1111: если определенные виды операций и преобразований безопасны с одними типами, но не с другими, то наличие прямого определения этого типа и отклонение компилятором небезопасных операций и преобразований было бы лучше, чем изменение метакласса, который использовался в способы, которые подходят только для PODS, автоматически меняются на не-PODS. - person supercat; 13.10.2018
comment
@supercat Я не понял, извините. Я имел в виду что-то вроде template<class T> struct X { T t; };, действительного только тогда, когда T - это pod. То же самое можно применить к метаклассам, когда вы выполняете преобразование класса, которое может изменить его член таким образом, чтобы класс стал подом; либо случайно, либо нет. - person Rakete1111; 13.10.2018
comment
@ Rakete1111: Требования к тому, чтобы что-то было PODS, немного строже, чем требования к присутствию в struct; Самым большим требованием должна быть стандартная планировка. Понятие struct в C было связано с представлением о том, что каждый T занимает диапазон sizeof (T) последовательных байтов, а структура представляет собой конкатенацию таких последовательностей байтов с необязательным заполнением, в то время как понятие размера не имеет смысла для типов классов. - person supercat; 14.10.2018

Из C ++ FQA Lite:

[16.4] Почему я должен использовать новый вместо заслуживающего доверия старого malloc ()?

FAQ: new / delete вызывает конструктор / деструктор; new является типобезопасным, malloc - нет; new может быть переопределен классом.

FQA: достоинства new, упомянутые в FAQ, не являются достоинствами, потому что конструкторы, деструкторы и перегрузка операторов - это мусор (посмотрите, что происходит, когда у вас нет сборки мусора?), А проблема безопасности типов здесь действительно крошечная (обычно у вас есть для приведения void *, возвращаемого malloc, к правильному типу указателя, чтобы назначить его типизированной переменной-указателю, что может раздражать, но далеко не «небезопасно»).

Да, и использование заслуживающего доверия старого malloc позволяет использовать столь же надежный и старый realloc. Жаль, что у нас нет нового блестящего обновления оператора или что-то в этом роде.

Тем не менее, новое еще не настолько плохо, чтобы оправдать отклонение от общепринятого стиля, используемого во всем языке, даже если это язык C ++. В частности, классы с нетривиальными конструкторами будут вести себя фатально, если вы просто переадресовываете объекты. Так почему бы не использовать новое во всем коде? Люди редко перегружают оператора new, поэтому он, вероятно, не слишком сильно вам помешает. А если они все-таки перегрузят новыми, вы всегда можете попросить их остановиться.

Извини, я просто не удержался. :)

person Matthias Benkard    schedule 08.10.2008
comment
Я не могу воспринимать этот комментарий серьезно, поскольку он явно демонстрирует предвзятое отношение автора к C ++. C ++ - это язык, используемый для создания программного обеспечения, ориентированного на производительность, и сборщик мусора может только навредить его цели. Я не согласен со всем вашим ответом! - person Miguel; 27.11.2015
comment
@Miguel Вы пропустили шутку. - person Dan Bechard; 11.04.2016

Всегда используйте новое в C ++. Если вам нужен блок нетипизированной памяти, вы можете напрямую использовать оператор new:

void *p = operator new(size);
   ...
operator delete(p);
person Ferruccio    schedule 08.10.2008
comment
интересно, я всегда просто выделял массив беззнаковых символов, когда мне нужен такой буфер необработанных данных. - person Greg Rogers; 08.10.2008
comment
Осторожно, семантика должна быть такой: p_var = новый тип (инициализатор); Не размер. - person Brian R. Bondy; 09.10.2008
comment
Нет, если вы вызываете оператор new напрямую, тогда требуется количество байтов для выделения в качестве параметра. - person Ferruccio; 09.10.2008
comment
Хм не уверен, я никогда не слышал об этом синтаксисе. - person Brian R. Bondy; 09.10.2008
comment
@Greg - в любом случае работает одинаково. Мне просто нравится тот факт, что вызов оператора new подчеркивает, что я делаю низкоуровневое нетипизированное распределение. - person Ferruccio; 09.10.2008
comment
Противоположность operator new - operator delete. Вызов delete для выражения с типом void* не является четко определенным действием. - person CB Bailey; 09.10.2008

новый vs malloc ()

1) new - это оператор, а malloc() - функция.

2) new вызывает конструкторы, а malloc() - нет.

3) new возвращает точный тип данных, а malloc() возвращает void *.

4) new никогда не возвращает NULL (выдает при ошибке), а malloc() возвращает NULL.

5) Перераспределение памяти не обрабатывается new, а malloc() может

person Yogeesh H T    schedule 26.11.2015
comment
Привет! Что касается пункта 4), new может быть проинструктирован возвращать NULL в случае ошибки. char* ptr = new (std::nothrow) char [323232]; - person Singh; 07.10.2016
comment
6) new создает из аргументов конструктора, а malloc использует размер. - person Evan Moran; 16.10.2016
comment
также есть new функция - person Ma Ming; 18.10.2017
comment
Если бы вы были настолько склонны в C к перераспределению, я бы надеялся, что вы использовали бы realloc, а не malloc, и начали бы с вашей переменной-указателя, инициализированной равной NULL. С другой стороны, если вам нужен кусок памяти изменяемого размера в C ++, я бы предложил std::vector, а не _5 _... То или файл. - person autistic; 29.04.2018

Используйте malloc и free только для выделения памяти, которая будет управляться c-ориентированными библиотеками и API. Используйте new и delete (а также варианты []) для всего, что вы контролируете.

person dmckee --- ex-moderator kitten    schedule 08.10.2008
comment
Также обратите внимание, что хорошо написанная библиотека C будет скрывать malloc и free внутри, так должен работать программист на C. - person Dacav; 13.08.2010
comment
@dmckee у вас есть пример C ++ с использованием c-ориентированных библиотек malloc и free? - person milesma; 31.07.2013
comment
@Dacav: если функция C примет указатель на объект, который ей нужно будет продолжать использовать после возврата из функции, и у вызывающего не будет возможности узнать, когда объект все еще нужен, это было бы вполне разумно для функции чтобы указать, что указатель должен быть создан с помощью malloc. Точно так же, если такой функции, как strdup, необходимо создать объект и вернуть его вызывающей стороне, вполне разумно указать, что вызывающая сторона должна вызывать free для объекта, когда он больше не нужен. Как такие функции могут избежать раскрытия вызывающей стороне использования malloc / free? - person supercat; 06.04.2015
comment
@supercat, есть что-то по своей сути неправильное в том, что функция C принимает указатель на объекты, поскольку C вообще не знает об объектах. В общем, я считаю, что лучшим подходом является наличие семантических оболочек вокруг выделения / освобождения также и в C. Это может быть приемлемо, но менее гибко, если библиотека C просит вызывающего абонента предварительно выделить и / или освободить память. Если функция C делает это и заявляет о праве собственности на выделенную память, вам неявно требуется выделить ее с помощью malloc. - person Dacav; 06.04.2015
comment
@supercat Один из примеров повседневного пакета, который, я уверен, все использовали, - это libgmp. Если вы когда-либо использовали какое-либо шифрование с открытым исходным кодом или программное обеспечение, основанное на таком шифровании (что весьма вероятно), то вы, вероятно, использовали арифметическую библиотеку произвольной точности, которая должна увеличиваться и уменьшаться в собственных внутренних данных. Это делается с помощью функции инициализации ... и тогда вы должны задаться вопросом, как вы используете код C, который является libgmp, в C ++, без его перекомпиляции в C ++? Теперь, имея в виду (компоновщик), подумайте об этом ... зачем здравомыслящему человеку когда-либо вставлять malloc в C ++? - person autistic; 29.04.2018
comment
@Sebivor: Стандарт C перегружает термин объект для ряда различных, но связанных понятий, которые отличаются от того, как он используется в других контекстах. - person supercat; 29.04.2018
comment
Я думаю, что Dacav предлагает, чтобы библиотеки c, реализующие непрозрачные типы, предлагали возможность инициализации непрозрачного хранилища, чтобы детали того, кем эти объекты управляются изнутри, были полностью скрыты, и вызывающему абоненту даже не нужно было знать, какая схема управления памятью используется внутри. Но как бы вы ни относились к этому предписанию, существует много мест, где вызывающий должен выполнять распределение (начиная с unix FILEs). - person dmckee --- ex-moderator kitten; 29.04.2018
comment
Себивор: Я думаю, возможно, вы имели в виду свой комментарий к @Decav? - person supercat; 29.04.2018
comment
[Я имел в виду @Dacav] - person supercat; 29.04.2018
comment
@dmckee: Это Dacav сказал, что C вообще не знает об объектах. Ни основной язык C, ни Стандартная библиотека не имеют широкой концепции функций, которые создают постоянное хранилище или сущности как часть своей работы, кроме тех, чьей целью является создание таких сущностей (например, malloc, fopen и т. Д.); все другие функции, которые работают с такими объектами, ожидают, что они были созданы их вызывающей стороной, либо с использованием функций, единственной целью которых было такое создание, либо такими средствами, которые вызывающий считает нужным. - person supercat; 29.04.2018
comment
@Sebivor: Стандарт C перегружает термин для описания переменных, членов структуры, членов объединения, элементов массива, областей, выделенных malloc, и целей указателя, что приводит к проблемам в таких местах, как, например, 6.5p7, который ограничивает случаи, когда объект может получить доступ к его сохраненному значению. - person supercat; 29.04.2018
comment
@supercat Я предполагаю, что стандарт C ++ наследует определение стандартов C, поскольку в противном случае он, похоже, не имеет его, и для каждого компилятора C ++, который я когда-либо использовал, был совместимый C ABI, который включает такие вещи, как макет структуры и внутреннее представление типов. Кроме того, стандарт C ++ на самом деле говорит об основных типах, таких как массивы символов, как если бы они были объектами. - person autistic; 29.04.2018
comment
Подождите, нет, в C ++ есть определение объекта. Там сказано ясно и ясно ... Объект - это область хранения. - person autistic; 29.04.2018
comment
@Dacav В соответствии со стандартом C ++, раздел включает объектную модель C ++, мы можно увидеть определение для object: Объект - это область хранения. Аналогичное определение есть в стандарте C; char c; как в C, так и в C ++, эта переменная обозначает объект. Разница в том, что некоторые (но не все) объекты в C ++ также также полиморфны (потому что C ++ - объектно-ориентированный объект, в конце концов). Не делайте ошибки, полагая, что только объектно-ориентированный код может использовать объекты. - person autistic; 29.04.2018
comment
@Sebivor, эти сообщения довольно старые, эта ветка комментариев еще жива? Во всяком случае, я использую объектно-ориентированный дизайн и для программ на C, поэтому полностью с вами согласен. - person Dacav; 29.04.2018
comment
@Dacav Правда? Если вы разрабатываете функцию, которая требует определенной формы распределения (и вынуждает остальной мир использовать это распределение по требованию), это технически нарушение принципа инверсии зависимостей, одного из пяти основных принципов объектно-ориентированного проектирования. Не все, что вы можете делать в C ++, является объектно-ориентированным, и вы не так много можете сделать с чисто объектно-ориентированными конструкциями. Вы не можете составить логику (последовательность, выбор и итерацию) без (обычно) процедурных или функциональных конструкций. OO - это просто более конкретная форма одного из расширений. - person autistic; 30.04.2018

Чтобы ответить на ваш вопрос, вы должны знать разницу между malloc и new. Отличие простое:

malloc выделяет память, а new выделяет память И вызывает конструктор объекта, для которого вы выделяете память.

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

Также разница между free и delete такая же. Разница в том, что delete вызовет деструктор вашего объекта в дополнение к освобождению памяти.

person The Quantum Physicist    schedule 29.07.2017

Есть одна большая разница между malloc и new. malloc выделяет память. Это нормально для C, потому что в C кусок памяти является объектом.

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

new выделяет память и создает объект в этой области памяти. Для типов, не относящихся к POD, это означает вызов конструктора.

Если вы сделаете что-то вроде этого:

non_pod_type* p = (non_pod_type*) malloc(sizeof *p);

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

Если, с другой стороны, вы делаете:

non_pod_type* p = new non_pod_type();

Вы получаете указатель, который всегда действителен, потому что new создал объект.

Даже для типов POD между ними есть существенная разница:

pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;

Этот фрагмент кода будет печатать неопределенное значение, потому что объекты POD, созданные malloc, не инициализированы.

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

pod_type* p = new pod_type();
std::cout << p->foo; // prints 0

Если вы действительно этого хотите, вы можете использовать new для получения неинициализированных объектов POD. См. этот другой ответ для получения дополнительной информации.

Еще одно отличие - поведение при неудаче. Когда не удается выделить память, malloc возвращает нулевой указатель, а new выдает исключение.

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

По этим причинам в коде C ++ следует использовать new, а не malloc. Но даже в этом случае вы не должны использовать new «открыто», потому что это привлекает ресурсы, которые вам нужно высвободить позже. Когда вы используете new, вы должны немедленно передать его результат в класс управления ресурсами:

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
person Community    schedule 01.11.2011

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

Например:

 std::vector<int> *createVector(); // Bad
 std::vector<int> createVector();  // Good

 auto v = new std::vector<int>(); // Bad
 auto result = calculate(/*optional output = */ v);
 auto v = std::vector<int>(); // Good
 auto result = calculate(/*optional output = */ &v);

Начиная с C ++ 11, у нас есть std::unique_ptr для работы с выделенной памятью, которая содержит владение выделенной памятью. std::shared_ptr был создан для случаев, когда вам нужно разделить владение. (вам понадобится это меньше, чем вы ожидаете от хорошей программы)

Создание экземпляра становится действительно простым:

auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::unique_ptr<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::unique_ptr<Class[]>(new Class[](42)); // C++11

C ++ 17 также добавляет std::optional, что может помешать вам требовать выделения памяти.

auto optInstance = std::optional<Class>{};
if (condition)
    optInstance = Class{};

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

 auto vector = std::vector<std::unique_ptr<Interface>>{};
 auto instance = std::make_unique<Class>();
 vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)

Итак, когда вам еще понадобится new? Практически никогда с C ++ 11. В большинстве случаев вы используете std::make_unique, пока не дойдете до точки, когда вы попадете в API, который передает право собственности через необработанные указатели.

 auto instance = std::make_unique<Class>();
 legacyFunction(instance.release()); // Ownership being transferred

 auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr

В C ++ 98/03 вам придется вручную управлять памятью. В этом случае попробуйте выполнить обновление до более новой версии стандарта. Если вы застряли:

 auto instance = new Class(); // Allocate memory
 delete instance;             // Deallocate
 auto instances = new Class[42](); // Allocate memory
 delete[] instances;               // Deallocate

Убедитесь, что вы правильно отслеживаете право собственности, чтобы не было утечек памяти! Семантика перемещения тоже пока не работает.

Итак, когда нам нужен malloc в C ++? Единственная веская причина - выделить память и инициализировать ее позже путем размещения new.

 auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
 auto instance = new(instanceBlob)Class{}; // Initialize via constructor
 instance.~Class(); // Destroy via destructor
 std::free(instanceBlob); // Deallocate the memory

Несмотря на то, что вышеизложенное верно, это также можно сделать с помощью оператора new. std::vector - хороший тому пример.

Наконец, у нас все еще есть слон в комнате: C. Если вам нужно работать с C-библиотекой, в которой память выделяется в коде C ++ и освобождается в коде C (или наоборот), вы вынуждены использовать malloc / free.

Если вы в этом случае, забудьте о виртуальных функциях, функциях-членах, классах ... Разрешены только структуры с POD.

Некоторые исключения из правил:

  • Вы пишете стандартную библиотеку с расширенными структурами данных, где подходит malloc
  • Вам нужно выделить большой объем памяти (в памяти копия файла размером 10 ГБ?)
  • У вас есть инструменты, которые не позволяют использовать определенные конструкции.
  • Вам нужно хранить неполный тип
person JVApen    schedule 22.12.2018

Есть несколько вещей, которые new делают, а malloc не делают:

  1. new создает объект, вызывая конструктор этого объекта
  2. new не требует приведения типов выделенной памяти.
  3. Он не требует выделения определенного количества памяти, а требует создания ряда объектов.

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

person herohuyongtao    schedule 15.01.2014

Если вы работаете с данными, которые не нуждаются в построении / уничтожении и требуют перераспределения (например, большой массив целых чисел), то я считаю, что malloc / free - хороший выбор, поскольку он дает вам перераспределение, что намного быстрее, чем new-memcpy -delete (это на моем компьютере с Linux, но я предполагаю, что это может зависеть от платформы). Если вы работаете с объектами C ++, которые не являются POD и требуют построения / уничтожения, вы должны использовать операторы new и delete.

В любом случае, я не понимаю, почему вы не должны использовать оба (при условии, что вы освобождаете свою malloced память и удаляете объекты, выделенные с помощью new), если можете воспользоваться преимуществом повышения скорости (иногда значительного, если вы повторно блокируете большие массивы POD), который может дать вам realloc.

Если вам это не нужно, вы должны придерживаться new / delete в C ++.

person PSkocik    schedule 09.04.2013

Если у вас есть код C, который вы хотите перенести на C ++, вы можете оставить в нем любые вызовы malloc (). Для любого нового кода C ++ я бы рекомендовал использовать вместо него new.

person Fred Larson    schedule 08.10.2008

new инициализирует значения структуры по умолчанию и правильно связывает содержащиеся в ней ссылки с собой.

E.g.

struct test_s {
    int some_strange_name = 1;
    int &easy = some_strange_name;
}

Таким образом, new struct test_s вернет инициализированную структуру с рабочей ссылкой, в то время как версия malloc'ed не имеет значений по умолчанию, а внутренние ссылки не инициализируются.

person kungfooman    schedule 14.12.2016

Если вы используете C ++, попробуйте использовать new / delete вместо malloc / calloc, поскольку они являются операторами. Для malloc / calloc вам нужно включить другой заголовок. Не смешивайте два разных языка в одном коде. Их работа во всех отношениях схожа, оба динамически распределяют память из сегмента кучи в хеш-таблице.

person user3488100    schedule 02.04.2014

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

person Peiti Li    schedule 14.08.2011
comment
new обычно не инициализирует память, хотя есть способы сделать это: см. stackoverflow.com/questions/2204176/ для одного обсуждения этого. - person wjl; 30.09.2011

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

class  B  {
private:
    B *ptr;
    int x;
public:
    B(int n)  {
        cout<<"B: ctr"<<endl;
        //ptr = new B;  //keep calling ctr, result is segmentation fault
        ptr = (B *)malloc(sizeof(B));
        x = n;
        ptr->x = n + 10;
    }
    ~B()  {
        //delete ptr;
        free(ptr);
        cout<<"B: dtr"<<endl;
    }
};
person Barry    schedule 17.08.2012

Операторы new и delete могут работать с классами и структурами, тогда как malloc и free работают только с блоками памяти, которые необходимо преобразовать.

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

person selwyn    schedule 08.10.2008

Редкий случай использования malloc / free вместо new / delete - это когда вы выделяете, а затем перераспределяете (простые типы модулей, а не объекты) с использованием realloc, поскольку в C ++ нет аналогичной функции для realloc (хотя это можно сделать с помощью больше подход C ++).

person Florentino Tuason    schedule 29.07.2017

malloc () используется для динамического выделения памяти в C, в то время как та же работа выполняется new () в C ++. Таким образом, вы не можете смешивать соглашения о кодировании двух языков. Было бы хорошо, если бы вы спросили разницу между calloc и malloc ()

person Hitesh Ahuja    schedule 26.07.2012
comment
Вы можете (но почти всегда не должны) использовать malloc в C ++. - person interjay; 26.07.2012
comment
Вы также упустили главный момент, что вы должны стремиться избегать динамического выделения памяти, если только это не делается с помощью интеллектуальных указателей. Вы просто настраиваете себя на боль другим разумом - person thecoshman; 11.10.2012