Ответ 1
Когда конструктор генерирует исключение, вызывается соответствующее удаление. Деструктор не вызван для класса, который был брошен, но любые компоненты класса, которые успешно создали их конструкторы, будут иметь их деструкторы.
Я вижу следующие конструкции:
new X
освободит память, если конструктор X
выбрал.
operator new()
может быть перегружен.
Каноническое определение новой перегрузки оператора void *operator new(size_t c, heap h)
и соответствующее operator delete
.
Наиболее распространенным оператором новой перегрузки является размещение new, которое void *operator new(void *p) { return p; }
Вы почти всегда не можете называть delete
указателем, заданным для размещения new
.
Это приводит к одному вопросу: как очищается память при срабатывании конструктора X
и используется перегруженный new
?
Когда конструктор генерирует исключение, вызывается соответствующее удаление. Деструктор не вызван для класса, который был брошен, но любые компоненты класса, которые успешно создали их конструкторы, будут иметь их деструкторы.
В принципе, если нет оператора delete
, который соответствует оператору new
, то ничего не делается. Ничего не сделано и в случае размещения нового, потому что соответствующий оператор удаления места размещения является no-op. Исключение не переадресовано: оно продолжает свой курс, поэтому вызывающий абонент имеет возможность (и ответственность) освобождать выделенную память.
Размещение new называется так, потому что оно используется для размещения объекта в памяти в противном случае; поскольку память не была приобретена новым оператором, маловероятно, что он может быть освобожден оператором удаления. На практике вопрос спорный, потому что (поскольку С++ 03, по крайней мере), не разрешается заменять новый оператор размещения (который имеет прототип operator new(size_t, void*)
или delete (operator delete(void*, void*)
). Оператор нового поставленного места размещения возвращает его второй аргумент, а поставленный оператор удаления места размещения - нет-op.
Другие операторы new
и delete
могут быть заменены либо глобально, либо для определенного класса. Если вызывается пользовательский оператор new
, а конструктор генерирует исключение, и существует соответствующий оператор delete
, то этот оператор удаления будет вызываться для очистки до того, как будет распространено исключение. Однако это не ошибка, если нет соответствующего оператора delete
.
Во-первых, пример:
#include <cstddef>
#include <iostream>
struct S
{
S(int i) { if(i > 42) throw "up"; }
static void* operator new(std::size_t s, int i, double d, char c)
{
std::cout << "allocated with arguments: "
<<i<<", "<<d<<", "<<c<<std::endl;
return new char[s];
}
static void operator delete(void* p, int i, double d, char c)
{
std::cout << "deallocated with arguments: "
<<i<<", "<<d<<", "<<c<<std::endl;
delete[] (char*)p;
}
static void operator delete(void* p)
{
std::cout << "deallocated w/o arguments"<<std::endl;
delete[] (char*)p;
}
};
int main()
{
auto p0 = new(1, 2.0, '3') S(42);
S* p1 = nullptr;
try
{
p1 = new(4, 5.0, '6') S(43);
}catch(const char* msg)
{
std::cout << "exception: "<<msg<<std::endl;
}
delete p1;
delete p0;
}
Вывод:
allocated with arguments: 1, 2, 3 allocated with arguments: 4, 5, 6 deallocated with arguments: 4, 5, 6 exception: up deallocated w/o arguments
Каноническое определение новой перегрузки оператора
void *operator new(std::size_t, heap h)
Я не вижу, как это канонически, так как это не разрешено:
Итак, теперь это действительная форма размещения new
:)
[basic.stc.dynamic.allocation]/1
Функция распределения должна быть функцией-членом класса или глобальной функцией; программа плохо сформирована, если функция распределения объявлена в области пространства имен, отличной от глобальной области действия, или объявлена статической в глобальной области. Тип возврата должен быть
void*
. Первый параметр должен иметь типstd::size_t
. Первый параметр не должен иметь связанного аргумента по умолчанию. Значение первого параметра должно интерпретироваться как запрошенный размер выделения.
[акцент мой]
Вы можете перегрузить функцию распределения, которая будет вызываться для формы размещения new
, см. [expr.new] (она явно не разрешена в [basic.stc.dynamic.allocation] для функций без шаблона, но также не запрещено). Размещение, указанное в new(placement)
, обобщается здесь на список выражений. Каждое выражение в списке выражений для конкретного нового выражения передается в качестве дополнительных аргументов функции распределения. Если вызывается функция освобождения (например, поскольку вызываемый ctor выдает исключение), те же аргументы плюс ведущий void*
(возвращаемое значение функции распределения) передаются функции дезактивации.
[expr.new]/18 состояний:
Если какая-либо часть инициализации объекта, описанная выше, завершается сбросом исключения, для объекта было получено хранилище, и может быть найдена подходящая функция освобождения, функция освобождения вызывается для освобождения памяти, в которой находился объект после чего исключение продолжает распространяться в контексте нового выражения. Если не удается найти однозначную функцию дезадаптации, распространение этого исключения не приведет к освобождению памяти объектов. [Примечание: Это удобно, если вызываемая функция выделения не выделяет память; в противном случае это может привести к утечке памяти. - конечная нота]
и/21
Если новое выражение вызывает функцию освобождения, оно передает значение, возвращаемое из вызова функции распределения, в качестве первого аргумента типа
void*
. Если вызывается функция освобождения места размещения, ему передаются те же дополнительные аргументы, которые были переданы функции распределения места размещения, то есть те же аргументы, что и те, которые указаны в синтаксисе нового места размещения.
и/20
Объявление функции освобождения места размещения соответствует объявлению функции распределения места размещения, если оно имеет одинаковое количество параметров и после преобразований параметров все типы параметров, кроме первого, идентичны. Любая функция освобождения места размещения соответствует функции распределения места размещения. Если поиск находит одну подходящую функцию освобождения, эта функция будет вызвана; иначе не будет вызываться функция освобождения. Если поиск находит двухпараметрическую форму обычной функции освобождения, и эта функция, рассматриваемая как функция освобождения места размещения, была бы выбрана в качестве соответствия для функции распределения, программа плохо сформирована. [Пример:
struct S { // Placement allocation function: static void* operator new(std::size_t, std::size_t); // Usual (non-placement) deallocation function: static void operator delete(void*, std::size_t); }; S* p = new (0) S; // ill-formed: non-placement deallocation function matches // placement allocation function
- конец примера]
Возвращаясь к [basic.stc.dynamic.deallocation]:
1 Функции освобождения должны быть функциями класса или глобальными функциями; программа плохо сформирована, если функции освобождения объявлены в области пространства имен, отличной от глобальной области или объявленной статикой в глобальной области.
2 Каждая функция освобождения должна возвращать
void
, а ее первый параметр должен бытьvoid*
. Функция дезадаптации может иметь более одного параметра.
"размещение нового" не является перегруженной версией нового, но является одним из вариантов нового оператора, а также не может быть перегружен.
Смотрите здесь список новых операторов здесь, а также описание того, как они перегружаются.
Если конструктор создает исключение при использовании места размещения new, компилятор знает, какой новый оператор был использован, и удаление места размещения.
Когда конструкция объекта, создаваемого как часть нового выражения, не выполняется, будет вызвана соответствующая функция освобождения - если она есть -. Например,
new X;
будет использовать следующую пару функций распределения/освобождения.
void * operator new(std::size_t);
void operator delete(void *);
Аналогично, для размещения нового вида
new(&a) X;
будут использоваться версии размещения operator new
и operator delete
.
void * operator new(std::size_t, void *);
void operator delete(void *, void *);
Обратите внимание, что последняя функция намеренно не выполняет никаких действий.