Ответ 1
Я просто собираюсь направить вас на этот ответ: В чем разница между новым/удаленным и malloc/бесплатным?. Мартин предоставил отличный обзор. Быстрый обзор того, как они работают (без погружения в то, как вы можете перегрузить их как функции-члены):
новое выражение и выделение
- Код содержит новое выражение, предоставляющее идентификатор типа.
- Компилятор рассмотрит, перегружает ли оператор новый с помощью функции распределения.
- Если он обнаруживает перегрузку новой функции распределения оператора, ее вызывается с использованием аргументов, данных для new и sizeof (TypeId) в качестве первого аргумента:
Пример:
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 не может выделить хранилище, он может вызвать
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)
.