Я знаю, как они отличаются синтаксически, и что С++ использует новые, а C использует malloc. Но как они работают, в объяснении высокого уровня?
См. В чем разница между новым/удаленным и malloc/бесплатным?
Я знаю, как они отличаются синтаксически, и что С++ использует новые, а C использует malloc. Но как они работают, в объяснении высокого уровня?
См. В чем разница между новым/удаленным и malloc/бесплатным?
Я просто собираюсь направить вас на этот ответ: В чем разница между новым/удаленным и malloc/бесплатным?. Мартин предоставил отличный обзор. Быстрый обзор того, как они работают (без погружения в то, как вы можете перегрузить их как функции-члены):
Пример:
new (a, b, c) TypeId;
// the function called by the compiler has to have the following signature:
operator new(std::size_t size, TypeOfA a, TypeOfB b, TypeOf C c);
new_handler
и надеяться, что это произойдет. Если места по-прежнему недостаточно, новый должен выбросить std::bad_alloc
или получить от него. Распределитель, который имеет throw()
(гарантия отсутствия броска), в этом случае возвращает нулевой указатель.Есть несколько специальных функций выделения, заданных специальными именами:
no-throw
новый. В качестве второго аргумента требуется nothrow_t
. Новое выражение формы следующего вида вызовет функцию распределения, принимающую только std:: size_t и nothrow_t:Пример:
new (std::nothrow) TypeId;
placement new
. Это принимает указатель void * как первый аргумент, и вместо того, чтобы возвращать вновь выделенный адрес памяти, он возвращает этот аргумент. Он используется для создания объекта по заданному адресу. Стандартные контейнеры используют это для предопределения пространства, но только при необходимости создают объекты.код:
// the following function is defined implicitly in the standard library
void * operator(std::size_t size, void * ptr) throw() {
return ptr;
}
Если функция распределения возвращает хранилище, а конструктор объекта, созданного броском выполнения, то оператор delete вызывается автоматически. В случае использования новой формы, которая принимает дополнительные параметры, такие как
new (a, b, c) TypeId;
Затем вызывается оператор delete, который принимает эти параметры. Эта версия удаления оператора вызывается только в том случае, если удаление завершено, потому что конструктор объекта бросил. Если вы вызываете delete самостоятельно, тогда компилятор будет использовать функцию нормального удаления оператора, используя только указатель void*
:
int * a = new int;
=> void * operator new(std::size_t size) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();
TypeWhosCtorThrows * a = new ("argument") TypeWhosCtorThrows;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
=> void operator delete(void * ptr, char const* arg1) throw();
TypeWhosCtorDoesntThrow * a = new ("argument") TypeWhosCtorDoesntThrow;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();
Если вы делаете
new (possible_arguments) TypeId[N];
Компилятор использует функции operator new[]
вместо обычного operator new
. Оператору может быть передан первый аргумент не точно sizeof(TypeId)*N
: компилятор мог бы добавить некоторое пространство для хранения количества созданных объектов (необязательно для вызова деструкторов). Стандарт ставит это так:
new T[5]
приводит к вызову оператора new[](sizeof(T)*5+x)
иnew(2,f) T[5]
приводит к вызову оператора new[](sizeof(T)*5+y,2,f)
.То, что new
делает иначе malloc
, выглядит следующим образом:
operator new
. Такое поведение можно адаптировать, перегружая этот оператор либо для всех типов, либо только для вашего класса.Таким образом, в целом new
настраивается, а также выполняет инициализацию, помимо распределения памяти. Это две большие различия.
Хотя malloc
/free
и new
/delete
имеют разные типы поведения, оба они делают то же самое на низком уровне: управляют динамически распределенной памятью. Я предполагаю, что это то, о чем вы действительно спрашиваете. В моей системе new
на самом деле вызывает malloc
внутренне для выполнения его выделения, поэтому я просто расскажу о malloc
.
Фактическая реализация malloc
и free
может сильно различаться, поскольку существует множество способов реализации выделения памяти. Некоторые подходы улучшают производительность, некоторые теряют память, другие лучше подходят для отладки. Собранные мусором языки могут также иметь совершенно разные способы выделения, но ваш вопрос касался C/С++.
В общем случае блоки выделяются из кучи, большой области памяти в адресном пространстве программы. Библиотека управляет кучей для вас, обычно используя системные вызовы, такие как sbrk
или mmap
. Один подход к распределению блоков из кучи состоит в том, чтобы поддерживать список свободных и выделенных блоков, в которых хранятся размеры блоков и местоположения. Первоначально список может содержать один большой блок для всей кучи. Когда запрашивается новый блок, распределитель выберет свободный блок из списка. Если блок слишком велик, его можно разделить на два блока (один из запрошенных размеров, другой - любой размер). Когда выделенный блок освобождается, его можно объединить с смежными свободными блоками, поскольку наличие одного большого свободного блока более полезно, чем несколько небольших свободных блоков. Фактический список блоков может быть сохранен как отдельные структуры данных или встроен в кучу.
Существует много вариантов. Возможно, вам захочется сохранить отдельные списки свободных и выделенных блоков. Вы можете получить более высокую производительность, если у вас есть отдельные области кучи для блоков общих размеров или отдельных списков для этих размеров. Например, когда вы выделили 16-байтовый блок, распределитель может иметь специальный список из 16-байтовых блоков, поэтому выделение может быть O (1). Также может быть полезно иметь дело только с размерами блоков, которые имеют степень 2 (все остальное округляется). Например, Buddy allocator работает таким образом.
"новый" делает намного больше, чем malloc. malloc просто выделяет память - она даже не равна нулю для вас. новые объекты инициализации, вызовы contructors и т.д. Я бы заподозрил, что в большинстве реализаций новый - это немного больше, чем тонкая оболочка вокруг malloc для базовых типов.
В C: malloc выделяет кусок памяти размера, который вы предоставляете в аргументе, и возвращает указатель на эту память.
Память объявляется в куче, поэтому не забудьте освободить ее, когда закончите.