Как действительно удаляет, дифференцируются между встроенными типами данных и определяемыми пользователем?

Если я делаю это:

// (1.)
int* p = new int;
//...do something
delete p;

// (2.)
class sample
{
public:
sample(){}
~sample(){}
};
sample* pObj = new sample;
//...do something
delete pObj;

Затем, как компилятор C++ знает тот объект после delete встроенный тип данных или объект класса?

Мой другой вопрос состоит в том что если я new указатель на массив intи затем я delete [] затем, как компилятор знает, что размер блока памяти освобождает?

6
задан Sam 14 August 2014 в 11:34
поделиться

6 ответов

  1. Компилятор знает тип указателя на объект, потому что он знает тип указателя:

    • p - это int*, поэтому указатель на объект будет int.
    • pObj является sample*, поэтому указанный объект будет sample.
  2. Компилятор не знает, указывает ли ваш int* p на единственный int объект или на массив (int[N]). Поэтому вы должны помнить, что для массивов нужно использовать delete[] вместо delete.

    Размер блока памяти для деаллокации и, самое главное, количество объектов для уничтожения известны, потому что new[] хранит их где-то, а delete[] знает, где получить эти значения. Этот вопрос из C++ FAQ Lite показывает две распространенные техники реализации new[] и delete[].

4
ответ дан 9 December 2019 в 22:33
поделиться

Он знает разницу между ними из-за типа указателя, который вы ему передаете: поведение undefined для передачи другого типа указателя, чем вы назначили (за исключением того, что вы можете передать указатель на базовый класс, если деструктор конечно виртуальный ).

Размер массива будет где-то храниться. Это похоже на C, где вы можете выделить определенный объем памяти, а затем освободить - среда выполнения должна будет знать размер, выделенный ранее.

Например, он может хранить количество элементов до того, как был выделен буфер. Стандарт явно позволяет компилятору передавать другой размер запроса в функцию распределения ( operator new [] ) в случае выделения массива - это может использоваться компилятором для вставки счетчика и смещения адрес, возвращаемый выражением new размером этого счетчика.

4
ответ дан 9 December 2019 в 22:33
поделиться

Нет!

Все, что делает delete , это то, что он вызывает деструктор типа, который в случае примитивных типов является «бездействием». Затем он передает указатель на :: operator delete (или на перегруженную версию, если хотите), и этот operator возвращает обратно память (проблема диспетчера памяти). То есть вы можете легко написать свой собственный менеджер памяти на C ++, если хотите, язык предоставляет его по умолчанию!

1
ответ дан 9 December 2019 в 22:33
поделиться
  1. Компилятор знает тип удаляемого объекта и пишет для вас другой код для достижения правильных результатов:
    • delete p может вызывать удаление во время выполнения с размером int.
    • delete pObj может сначала вызвать pObj-> ~ sample (), а затем удалить с размером sample
  2. Я думаю, что с массивами существует скрытое значение размера массива, поэтому может быть, что весь массив удаляется за один раз.
1
ответ дан 9 December 2019 в 22:33
поделиться

Тогда откуда компилятор C++ знает, что объект после удаления является встроенным типом данных или объектом класса?

Потому что во время компиляции компилятор отслеживает типы каждого объекта и вставляет соответствующий код.

Мой другой вопрос заключается в том, что если я создаю указатель на массив int, а затем удаляю [], то как компилятор узнает размер блока памяти для деаллокации?

Никак. За этим следит система времени выполнения.
Когда вы динамически выделяете массив, библиотека времени выполнения связывает размер объекта с объектом, таким образом, когда она удаляет его, она знает (просматривая связанное значение) его размер.

Но я полагаю, вы хотите знать, как она выполняет ассоциацию?
. Это зависит от системы и является деталью реализации. Но простая стратегия заключается в выделении дополнительных 4 байт для хранения размера в первых четырех байтах, затем возвращается указатель на выделенный 4-й байт. Когда вы удаляете указатель, вы знаете, что размер - это 4 байта перед указателем. Примечание: я не утверждаю, что ваша система использует эту технику, но это одна из стратегий.

1
ответ дан 9 December 2019 в 22:33
поделиться

Для первой (не массива) части вопроса, ответы выше, указывающие на то, что компилятор вставляет код для удаления соответствующего количества байт на основе типа указателя, не дают мне четкого ответа... оператор delete 1) вызывает деструктор, если это применимо, а затем 2) вызывает функцию "operator delete()"... именно оператор delete фактически удаляет. Я вижу, что в части (1) играет роль сгенерированный компилятором код, т.е. адрес назначения деструктора должен быть вставлен. Но в части (2), это уже существующая библиотечная функция, обрабатывающая деаллокацию, так как же она узнает размер данных? Глобальный оператор delete - который, как я полагаю, используется во всех случаях, если только программист не определил версию класса-члена/перегруженного глобального оператора - принимает только аргумент void*, указывающий начало данных, так что ему даже нельзя передать размер данных. Я читал вещи, указывающие на идею генерируемого компилятором кода, а также вещи, предлагающие, чтобы глобальный оператор delete для не-массивов просто использовал free(), т.е. он знает размер данных не по типу указателя, а просматривая несколько байт до самих данных, где размер будет сохранен new/malloc. Последнее - единственное решение, которое имеет смысл для меня, но, возможно, кто-то может просветить меня по-другому...

0
ответ дан 9 December 2019 в 22:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: