В каких случаях я использую malloc против new? - PullRequest
430 голосов
/ 08 октября 2008

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

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

Ответы [ 19 ]

342 голосов
/ 08 октября 2008

Если вы не вынуждены использовать 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
132 голосов
/ 01 ноября 2011

Короткий ответ: не используйте 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 для начала!

50 голосов
/ 09 октября 2008

Из C ++ FQA Lite :

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

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

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

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

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

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

48 голосов
/ 08 октября 2008

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

void *p = operator new(size);
   ...
operator delete(p);
30 голосов
/ 08 октября 2008

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

24 голосов
/ 26 ноября 2015

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

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

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

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

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

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

13 голосов
/ 29 июля 2017

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

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

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

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

9 голосов
/ 01 ноября 2011

Существует одна большая разница между 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
6 голосов
/ 15 января 2014

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

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

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

4 голосов
/ 09 апреля 2013

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

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

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

...