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

428

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

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

  • 29
    Я просто хотел бы добавить напоминание о том, что вы не можете смешивать два стиля - то есть вы не можете использовать new для создания объекта и затем вызывать free () для него, а также пытаться удалить блок, выделенный malloc (). Наверное, очевидно, чтобы сказать это, но тем не менее ...
  • 29
    Хорошие ответы, все, что я должен добавить (что я не видел), это то, что new / delete вызывает для вас конструктор / деструктор, а malloc / free - нет. Просто стоит упомянуть разницу.
Показать ещё 2 комментария
Теги:
malloc
memory-management
new-operator

19 ответов

351

Если вы не вынуждены использовать 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 может более эффективно увеличить размер фрагмента памяти.

Стоит отметить, что вы не можете смешивать new/free и malloc/delete.

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

int* p_scalar = new int(5);  // Does not create 5 elements, but initializes to 5
int* p_array  = new int[5];  // Creates 5 elements
  • 2
    Что касается вызова delete foo, когда вы должны вызывать delete [] foo, некоторые компиляторы исправят это автоматически, а не утечки, а другие будут удалять только первую запись и утечки. У меня было несколько таких в некотором коде, и valgrind найдет их для вас.
  • 1
    Я проверил это много лет назад на VC ++ с массивом объектов и установил точку останова в моем деструкторе. Только первый был удален. Но да, хорошая мысль о том, что некоторые компиляторы ловят это для вас.
Показать ещё 21 комментарий
123

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

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

  • malloc не является типичным каким-либо значимым образом. В С++ вы должны вернуть результат из 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
    }
    
  • Это хуже, чем это. Если тип, о котором идет речь, POD (простые старые данные), вы можете полу-разумно использовать malloc для выделения памяти для него, поскольку f2 делает в первом пример.

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

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

    сделает malloc of f2 также плохим, без какой-либо очевидной диагностики. Пример здесь тривиален, но можно случайно ввести не-PODness намного дальше (например, в базовом классе, добавив не-POD-элемент). Если у вас есть С++ 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)));
    }
    

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

  • malloc возвращает NULL, если сбой распределения. new будет бросать std::bad_alloc. Поведение позже с помощью указателя NULL имеет значение undefined. Исключение имеет чистую семантику, когда она выбрасывается, и она выбрасывается из источника ошибки. Обертка 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;
    }
    
  • По существу malloc - это функция C, а new - это функция С++. В результате 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();
    }
    
  • Наша функция 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 для начала!

  • 21
    Жаль, что в C ++ struct и class означают одно и то же; Интересно, были ли бы какие-то проблемы с тем, чтобы struct была зарезервирована для POD и, возможно, все типы class считались не POD. Любые типы, определенные кодом, предшествующим изобретению C ++, обязательно будут POD, поэтому я не думаю, что проблема обратной совместимости будет там. Есть ли преимущества для того, чтобы не-POD-типы были объявлены как struct а не class ?
  • 0
    @supercat Немного поздно, но, как оказалось, заставить struct и class делать практически одно и то же было замечательным дизайнерским решением, которое теперь включает в себя замечательную функцию под названием «metaclasses» (от Herb) .
Показать ещё 5 комментариев
48

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

void *p = operator new(size);
   ...
operator delete(p);
  • 3
    Интересно, я всегда просто выделяю массив беззнаковых символов, когда мне нужен такой буфер необработанных данных.
  • 0
    Осторожно, семантика должна быть такой: p_var = new type (initializer); Не размер.
Показать ещё 4 комментария
46

Из С++ FQA Lite:

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

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

FQA: Достоинства нового, упомянутого в Часто задаваемые вопросы не являются добродетелями, потому что конструкторы, деструкторы и Перегрузка оператора - это мусор (см. что происходит, когда у вас нет мусора сбор?), и тип безопасности вопрос действительно крошечный здесь (обычно вы должны отбросить пустоту *, возвращенную malloc к правильному типу указателя к назначьте его типизированной переменной указателя, что может быть раздражающим, но далеким от "Небезопасный" ).

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

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

Извините, я просто не мог сопротивляться.:)

  • 6
    Это бунт ! Благодарю.
  • 6
    Я не могу воспринимать этот комментарий серьезно, так как он явно проецирует предвзятость автора в отношении C ++. C ++ - это язык, используемый для создания программного обеспечения, ориентированного на производительность, и сборщик мусора может только нанести ущерб его цели. Я не согласен с вашим полным ответом!
Показать ещё 1 комментарий
26

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

  • 9
    Также обратите внимание, что хорошо написанная C-библиотека скрывает malloc и освобождает внутренне, именно так должен работать C-программист.
  • 0
    @ dmckee у вас есть пример C ++ с использованием библиотек c-centric от malloc и free?
Показать ещё 15 комментариев
22

новый vs malloc()

1) new является оператором, а malloc() является функцией.

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

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

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

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

  • 4
    Привет, для пункта 4), new может быть проинструктирован возвращать NULL в случае неудачи. char* ptr = new (std::nothrow) char [323232];
  • 0
    6) new создает из аргументов конструктора, в то время как malloc использует размер.
Показать ещё 2 комментария
12

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

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

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

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

9

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

В С++, если вы не имеете дело с типами POD (которые похожи на типы C), вы должны вызвать конструктор в ячейке памяти, чтобы на самом деле был объект. Не-POD-типы очень распространены в С++, так как многие возможности С++ делают объект автоматически не-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 генерирует исключение.

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

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

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
6

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

  • new создает объект, вызывая конструктор этого объекта
  • new не требует типизации выделенной памяти.
  • Он не требует выделения объема памяти, а требует нескольких объекты, которые будут созданы.

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

4

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

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

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

3

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

3

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

1

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

например.

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

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

1

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

  • 4
    new вообще не инициализирует память, хотя есть способы сделать это: см. stackoverflow.com/questions/2204176/… для одного обсуждения об этом.
0

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

Например:

 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 был создан для std::shared_ptr когда вы должны делиться собственностью. (вам нужно это меньше, чем вы ожидаете в хорошей программе)

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

auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<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 пока не 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

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

Итак, когда нам нужен 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 ГБ?)
  • У вас есть инструменты, мешающие вам использовать определенные конструкции
  • Вам нужно хранить неполный тип
0

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

0

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

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;
    }
};
0

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

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

-3

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

  • 1
    Вы можете (но почти всегда не должны) использовать malloc в C ++.
  • 1
    Вы также упустили главное, что вам следует стремиться избегать динамического выделения памяти, если это не делается с помощью умных указателей. Вы просто настраиваете себя на боль другим мудрым

Ещё вопросы

Сообщество Overcoder
Наверх
Меню