2008-10-31 7 views
23

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

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

int* p = new int; 

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

int* p = new int[1]; 

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

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

ответ

32

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

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

+4

Actualy, когда u использует новый int [1], он хранится на голове массива до того, как он будет первым, размером с него. Таким образом, использование delete вместо delete [] не освободит эту часть памяти. – Rodrigo 2009-04-24 13:35:03

2

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

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

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

+0

Не соглашайтесь с последним заявлением. Компилятор должен знать размер массива, чтобы вызвать деструктор для каждого объекта в массиве. Я думаю, вы смешиваете это с тем, что C++ не выполняет проверку границ на массивах. – 2008-10-31 03:39:19

+0

О, правда. Я думал, вы предполагаете, что размер будет храниться как часть структуры данных массива (буфера). Да, компилятор, вероятно, должен будет хранить информацию о размере где-нибудь ... – 2008-10-31 04:07:28

+0

Один из подходов состоит в том, чтобы сохранить размер и количество элементов в слове до начала массива. Это называется cookie. – Dynite 2008-10-31 09:16:01

6

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

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

8

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

Мой первоначальный ответ (что не попадает в точку) ::

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

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

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

Что случилось с тем, что если библиотеки компилятора & предполагают, что все объекты, выделенные «новыми», являются объектными массивами, было бы нормально вызвать «удалить» для отдельных объектов или массивов объектов. Одиночные объекты только бы частный случай массива объекта, имеющего счетчик 1. Может быть, есть что-то мне не хватает, так или иначе ...

5

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

массива delete[] не может использоваться в классе-указателе-на-основе, когда компилятор хранит количество объектов при вызове new[], он не сохраняет типы или размеры объектов (как указывал Дэвид, на C++ вы редко платите для функции, которую вы не используете). Однако, скалярная 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 
} 

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

0

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

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

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 работает в первом примере; Я знаю, что это не дает сообщение об ошибке компилятора.