it-roy-ru.com

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

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

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

431
Ralph Burgess

Если вы не вынуждены использовать 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
342
Brian R. Bondy

Краткий ответ: не используйте 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 на non-POD без каких-либо ошибок компиляции, что потенциально очень трудно для устранения проблем. Например, если кто-то (возможно, другой программист, во время обслуживания, гораздо позже должен был внести изменение, из-за которого foo больше не являлось POD, то во время компиляции не появилось бы явной ошибки, как вы надеетесь, например:

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

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

    Хотя boost --- невозможно определить, является ли тип 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 для начала!

132
Flexo

Из C++ FQA Lite :

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

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

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

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

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

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

50
Matthias Benkard

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

void *p = operator new(size);
   ...
operator delete(p);
48
Ferruccio

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

30
dmckee

новый против malloc ()

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

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

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

4) new никогда не возвращает NULL (сгенерирует при ошибке), в то время как malloc() возвращает NULL

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

24
Yogeesh H T

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

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

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

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

14
The Quantum Physicist

Существует одно большое различие между 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
10
R. Martinho Fernandes

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

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

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

6
herohuyongtao

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

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

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

4
PSkocik

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

3
user3488100

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

Например:

 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::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, пока не дойдете до точки, где вы нажмете 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++? Единственная действительная причина заключается в том, чтобы выделить память и инициализировать ее позже путем размещения нового.

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

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

2
Fred Larson

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

Например.

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

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

1
lama12345

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

1
Peiti Li

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

0
Florentino Tuason

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

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

0
selwyn