динамическое распределение — Понимание ‘delete []’: переполнение стека

Я наткнулся на пример:

#include <iostream>
#include <stdexcept>

using namespace std;

class A{
public:
A():m_n(m_object_id++){}
~A(){cout << m_n;}
private:
const int m_n;
static int m_object_id;
};

int A::m_object_id=0;int main()
{
A * const p = new A[3];
A * const q = reinterpret_cast<A * const> (new char[3*sizeof(A)]);
new (q) A;
new (q+1)A;
q->~A();
q[1].~A();
delete [] reinterpret_cast<char *>(q); // -> here
delete[] p;
cout << endl;

return 0;
}

выход:
34210

Может кто-нибудь объяснить delete [] reinterpret_cast<char *>(q); // -> hereЧто он делает и производит какой-либо вывод?

РЕДАКТИРОВАТЬ

Мой вопрос delete [] reinterpret_cast<char *>(q); не звонит ~A(){cout << m_n;} , лайк delete[] p;, почему так ?

1

Решение

q инициализируется с памятью из new char[3*sizeof(A)], но рассматривается как массив A, Эта память затем используется для выполнения размещения new на первых двух элементах этого массива.

A * const q = reinterpret_cast<A * const> (new char[3*sizeof(A)]);
new (q) A;
new (q+1)A;

После явного вызова деструкторов на построенном элементе, q отбрасывается назад к char *и память удаляется delete [], Цель этого акта — убедиться, что delete[] применяется к объекту того же типа, который был создан с помощью вызова new[] которому q точки.

q->~A();
q[1].~A();
delete [] reinterpret_cast<char *>(q); // -> here

Коллекция объектов, которые были выделены с new[], должен быть удален с delete[], Это потому что delete[] будет обрабатывать указатель как распределение массива и должным образом уничтожать каждый элемент массива.

2

Другие решения

delete [] вызывает деструктор для каждого элемента массива, а затем освобождает связанную с ним память. delete указатель на массив вызовет только деструктор первого элемента, а затем освободит всю связанную память.

Всегда используйте delete [] когда вы используете new [..],

Вызываемый деструктор определяется типом указателя, переданного delete или же delete [], В приведенном ниже примере:

A *object1 = new A();
B *object2 = new B();

delete (B*) object1;
delete (A*) object2;

(Потенциально) заставит весь ад вырваться, потому что ~B() вызывается в случае A и наоборот.

void *object = new A();
delete object;

не звонит ~A() потому что не знает object указывает на случай A, в то время как

void *object = new A();
delete reinterpret_cast<A*>(object);

правильно звонить ~A() перед освобождением связанной памяти.

2

new char[3*sizeof(A)] говорит: «выделите мне место, достаточно большое для 3 As, но подумайте об этом пространстве как о массиве символов / строке».

reinterpret_cast<A * const> говорит, что возьмите тот блок памяти, который вы только что выделили и который рассматриваете как строку, а теперь воспринимайте его как массив As.

delete [] reinterpret_cast<char *>(q); говорит теперь возьми тот блок памяти, который ты сейчас думать о нем как о массиве As и начать снова думать о нем как о массиве char, потому что так он был распределен, и вот как он должен быть удален. (то есть delete [] будет вызывать char dtor для каждого символа в этом массиве, а не вызывать A dtor)

Не делайте этого в реальном коде!

1

p выделяется как простой массив класса A, а затем удаляется.
q более сложный, потому что он выделяется как необработанная память, и затем в этой необработанной памяти создаются два элемента A с использованием размещения new. Эти два элемента удаляются по отдельности, а необработанная память удаляется оператором, о котором вы спрашиваете.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector