Как удаление различает встроенные типы данных и определяемые пользователем? - PullRequest
6 голосов
/ 27 февраля 2010

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

// (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, а затем I delete [], то как компилятор узнает размер блока памяти, который нужно выделить?

Ответы [ 6 ]

4 голосов
/ 27 февраля 2010
  1. Компилятор знает тип объекта, на который указывает указатель, потому что он знает тип указателя:

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

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

4 голосов
/ 27 февраля 2010

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

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

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

1 голос
/ 27 февраля 2010

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

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

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

Это не так. Это отслеживается системой времени выполнения.
Когда вы динамически выделяете массив, библиотека времени выполнения связывает размер объекта с объектом, таким образом, когда она удаляет его, она знает (просматривая соответствующее значение) размер.

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

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

Это не так!

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

0 голосов
/ 02 марта 2010

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

...