Вопрос

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

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

int* p = new int;

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

int* p = new int[1];

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

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

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

Массив 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 ++.

Маршалл Клайн имеет некоторую информацию по этой теме .

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

Я провел несколько экспериментов с его образцом кода (после исправления нескольких опечаток).Вот мои результаты.Опечатки:~Base требовалось тело функции Base *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
}

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

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
david@Godel: # 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
}

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

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./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 работает в первом примере;Я только знаю, что это не выдает сообщение об ошибке компилятора.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top