Я наткнулся на пример:
#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;
, почему так ?
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[]
будет обрабатывать указатель как распределение массива и должным образом уничтожать каждый элемент массива.
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()
перед освобождением связанной памяти.
new char[3*sizeof(A)]
говорит: «выделите мне место, достаточно большое для 3 As, но подумайте об этом пространстве как о массиве символов / строке».
reinterpret_cast<A * const>
говорит, что возьмите тот блок памяти, который вы только что выделили и который рассматриваете как строку, а теперь воспринимайте его как массив As.
delete [] reinterpret_cast<char *>(q);
говорит теперь возьми тот блок памяти, который ты сейчас думать о нем как о массиве As и начать снова думать о нем как о массиве char, потому что так он был распределен, и вот как он должен быть удален. (то есть delete [] будет вызывать char dtor для каждого символа в этом массиве, а не вызывать A dtor)
Не делайте этого в реальном коде!
p выделяется как простой массив класса A, а затем удаляется.
q более сложный, потому что он выделяется как необработанная память, и затем в этой необработанной памяти создаются два элемента A с использованием размещения new. Эти два элемента удаляются по отдельности, а необработанная память удаляется оператором, о котором вы спрашиваете.