Подтвердить что ты не робот

Зачем нам нужен оператор "delete []"?

Это вопрос, который время от времени натыкался на меня. Я всегда думал, что С++ должен быть спроектирован так, чтобы оператор "delete" (без скобок) работал даже с "новым []" оператором.

На мой взгляд, написав это:

int* p = new int;

должен быть эквивалентен распределению массива из 1 элемента:

int* p = new int[1];

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

Есть ли причина, по которой оператор "delete []" был введен в С++? Единственная причина, по которой я могу думать, заключается в том, что распределение массивов имеет небольшой объем памяти (вам нужно где-то хранить размер массива), так что отличия "delete" и "delete []" было небольшой оптимизацией памяти.

4b9b3361

Ответ 1

Это так, что вызовут деструкторы отдельных элементов. Да, для массивов POD нет большой разницы, но в С++ вы можете иметь массивы объектов с нетривиальными деструкторами.

Теперь, ваш вопрос: почему бы не сделать new и delete вести себя как new[] и delete[] и избавиться от new[] и delete[]? Я хотел бы вернуться к книге Stroustrup "Дизайн и эволюция", где он сказал, что если вы не используете возможности С++, вам не придется платить за них (по крайней мере, во время выполнения). Теперь он будет new или delete вести себя так же эффективно, как malloc и free. Если delete имел значение delete[], во время выполнения были бы дополнительные накладные расходы (как указал Джеймс Карран).

Ответ 2

Черт, я пропустил всю точку вопроса, но я оставлю свой первоначальный ответ в качестве опоры. Почему мы удалили [], потому что давным-давно мы удалили [cnt], даже сегодня, если вы пишете delete [9] или delete [cnt], компилятор просто игнорирует вещь между [], но компилирует ok. В то время С++ сначала обрабатывался интерфейсом, а затем загружался в обычный компилятор C. Они не могли сделать трюк хранения графа где-то под занавеской, возможно, они не могли даже думать об этом в то время. И для обратной совместимости компиляторы, скорее всего, использовали значение, заданное между [], как количество массивов, если нет такого значения, тогда они получили счетчик из префикса, поэтому он работал в обоих направлениях. Позже мы ничего не набрали между [], и все сработало. Сегодня я не думаю, что "delete []" необходимо, но реализации требуют этого.

Мой оригинальный ответ (который не соответствует точке)::

"delete" удаляет один объект. "delete []" удаляет массив объектов. Для удаления [] для работы реализация сохраняет количество элементов в массиве. Я просто проверил это путем отладки кода ASM. В тестируемой версии (VS2005) счетчик был сохранен как префикс массива объектов.

Если вы используете "delete []" на одном объекте, переменная count является мусором, поэтому код выходит из строя. Если вы используете "delete" для массива объектов, из-за некоторой несогласованности код выходит из строя. Я тестировал эти случаи только сейчас!

"delete просто удаляет память, выделенную для массива." выражение в другом ответе неверно. Если объект является классом, delete будет вызывать DTOR. Просто поместите точку останова в DTOR-код и удалите объект, точка останова ударит.

Что мне пришло в голову, так это то, что если компилятор и библиотеки предположили, что все объекты, выделенные "новым", являются объектными массивами, было бы нормально вызвать "delete" для отдельных объектов или массивов объектов. Единственные объекты просто были бы особым случаем массива объектов, имеющего счет 1. Возможно, что-то мне не хватает, во всяком случае...

Ответ 3

Поскольку все остальные, похоже, пропустили точку вашего вопроса, я просто добавлю, что я думал об этом несколько лет назад и никогда не получал ответа.

Единственное, о чем я могу думать, это то, что для обработки одного объекта в виде массива (ненужного "for(int i=0; i<1; ++i)" ) есть очень маленький лишний накладной ресурс

Ответ 4

Добавление этого, поскольку в настоящее время его нет в другом адресе:

Array delete[] не может использоваться в классе-указателе-на-основе, когда компилятор хранит количество объектов при вызове new[], он не сохраняет типы или размеры объектов (как Дэвид указал, что на С++ вы редко платите за ту функцию, которую вы не используете). Однако скалярный delete можно безопасно удалить через базовый класс, поэтому он используется как для нормальной очистки объекта, так и для полиморфной очистки:

struct Base { virtual ~Base(); };
struct Derived : Base { };
int main(){
    Base* b = new Derived;
    delete b; // this is good

    Base* b = new Derived[2];
    delete[] b; // bad! undefined behavior
}

Однако в противном случае - не виртуальный деструктор - скаляр delete должен быть как можно дешевле - он не должен проверять количество объектов и тип удаляемого объекта. Это делает удаление по встроенному типу или типу старых данных очень дешевым, поскольку компилятору нужно только вызывать ::operator delete и ничего больше:

int main(){
    int * p = new int;
    delete p; // cheap operation, no dynamic dispatch, no conditional branching
}

Хотя это не исчерпывающее рассмотрение распределения памяти, я надеюсь, что это поможет прояснить широту возможностей управления памятью, доступных на С++.

Ответ 6

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

Здесь хорошо читается: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=287

И нет, размеры массивов не хранятся нигде в С++. (Спасибо всем за то, что указали, что это утверждение неточно.)

Ответ 7

Я немного смущен ответом Аарона и откровенно признаю, что не понимаю, почему и где требуется удаление [].

Я провел несколько экспериментов с его примером кода (после исправления нескольких опечаток). Вот мои результаты.   Опоты: ~ Базе нужно тело функции   База * b была объявлена ​​дважды

struct Base { virtual ~Base(){ }>; };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good

<strike>Base</strike> b = new Derived[2];
delete[] b; // bad! undefined behavior
}

Компиляция и выполнение

[email protected]:g++ -o atest atest.cpp 
[email protected]: ./atest 
[email protected]: # No error message

Измененная программа с удалением [] удалена

struct Base { virtual ~Base(){}; };
struct Derived : Base { };

int main(){
    Base* b = new Derived;
    delete b; // this is good

    b = new Derived[2];
    delete b; // bad! undefined behavior
}

Компиляция и выполнение

[email protected]:g++ -o atest atest.cpp 
[email protected]: ./atest 
atest(30746) malloc: *** error for object 0x1099008c8: pointer being freed was n
ot allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

Конечно, я не знаю, действительно ли delete [] b работает в первом примере; Я знаю, что это не дает сообщение об ошибке компилятора.