Зачем вам писать подобное? (намеренно не используется delete [] в массиве)

Время от времени я сталкивался с таким кодом - я подозреваю, что создатель / боялся, что удаление таблицы будет повторяться по таблице и «стоить производительность» (что, по-моему, не будет выполнено в любом случае) ... есть ли реальный выгоду, которую можно получить / рассмотреть / представить, если не использовать здесь удаление таблицы?

myClass** table = new myClass* [size];
... //some code that does not reallocate or change the value of the table pointer ;)
delete table; // no [] intentionally

person RnR    schedule 24.04.2009    source источник


Ответы (8)


На самом деле нет причин так писать и серьезных причин никогда этого не делать.

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

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

Добавьте к этому, что эта оптимизация, зависящая от компилятора, непереносима.

Это тот случай, когда вы не получаете преимуществ, заменяя delete [] на delete, но рискуете сильно полагаться на неопределенное поведение.

person sharptooth    schedule 27.04.2009
comment
В качестве дополнительного примечания, если кто-то действительно очень хочет избежать этих накладных расходов, переносимым подходом будет использование malloc () и free () вместо new [] и delete [] - person bdonlan; 15.05.2009

Если вы сделаете это, вы получите то, что Стандарт C ++ называет неопределенным поведением - все может случиться.

person Community    schedule 24.04.2009

Это утечка памяти. new [] должен совпадать с delete []. Кроме того, поскольку table является указателем на первый элемент массива указателей, любой член массива, если это массив сам по себе, необходимо будет отменить с помощью delete [].

person dirkgently    schedule 24.04.2009
comment
к сожалению, утечки памяти нет - для встроенных типов таблица new и delete практично отображает malloc и бесплатные вызовы (нет никакой разницы, используете ли вы удаление таблицы или нет, поскольку десктрукторы не нужно вызывать в любом случае) - я говорю, к сожалению так как я бы предпочел, чтобы такие хаки вообще не работали :) - person RnR; 25.04.2009
comment
@RnR: Это полностью зависит от реализации ... и, конечно, не всегда. - person SoapBox; 25.04.2009
comment
Откуда берутся встроенные типы? Ваш пример состоит из myClass. И да, вы вызываете UB, как упомянул Нил. - person dirkgently; 25.04.2009
comment
@Soap: Ага - мне интересно, почему они разрешили неопределенное поведение, когда на самом деле они должны требовать, чтобы скомпилированный выдал ошибки: / - person RnR; 25.04.2009
comment
@dirkgently: необработанные указатели в C ++ - это практичные встроенные типы - по крайней мере, так показывает опыт (например, написание пользовательских распределителей и анализ стандартных) - нет конструкторов, деструкторов и т. д. - необработанный указатель не сильно отличается от int. - person RnR; 25.04.2009
comment
@dirkgently - Я согласен - я в основном собираю здесь боеприпасы, так как скоро собираюсь изменить эту строчку - вся ваша помощь действительно полезна и приветствуется :) - person RnR; 25.04.2009
comment
@RnR На самом деле компилятор не может обнаружить эту ошибку - person ; 25.04.2009
comment
@Neil: компилятор не смог его поймать, но система времени выполнения могла. - person Mark Ransom; 25.04.2009
comment
@Mark Ransom: В C ++ почти ничего нет во время выполнения. В этом своего рода суть C ++. Если вы не можете поймать это во время компиляции, значит, это проблема программиста. - person Michael Kohne; 25.04.2009
comment
Суть УБ в том, что диагностика не требуется. Во-первых, это потому, что вы не можете создать надежный. - person dirkgently; 25.04.2009
comment
(Кроме того, это получает отрицательные голоса слева и справа, оставят ли отрицательные голоса примечание?) - person dirkgently; 25.04.2009

Мало того, что в этом нет пользы, код просто неправильный - в лучшем случае он приводит к утечке памяти, а в худшем - может привести к сбою вашей программы или открытию труднодоступной дыры в безопасности. Вы всегда должны сопоставлять new с delete и new[] с delete[]. Всегда.

person Adam Rosenfield    schedule 24.04.2009

Это определенно неправильно, так как новый [] должен быть связан с delete []. Если вы этого не сделаете, вы получите неопределенное поведение.

Это может работать (частично), потому что большинство реализаций используют new для реализации new []. Единственная разница для такой реализации будет заключаться в том, что она вызовет только 1 деструктор (для первого элемента, а не для всех деструкторов. Но избегайте этого, поскольку это незаконно c ++.

person lothar    schedule 24.04.2009

Теоретически вы должны вызвать delete [].

РЕДАКТИРОВАТЬ: Следующее относится только к Microsoft Visual C ++ (я должен был это сказать).

На практике в Microsoft Visual C ++ не имеет значения, какое удаление вы используете, если у объектов в массиве нет деструкторов. Поскольку у вас есть массив указателей, а указатели не могут иметь деструкторов, все должно быть в порядке.

Однако, как указывали другие, в C ++ неправильно смешивать new [] и delete без []. Хотя в этом случае он может работать в Visual C ++, код не переносится и может не работать в других компиляторах.

Но возвращаясь к конкретному случаю Visual C ++, даже если вы вызовете delete [], компилятор поймет, что ему не нужно перебирать деструкторы вызова массива, когда это массив примитивных типов, таких как int, char или указатели. . Вызов delete в этом случае действительно работает и ничего не сломает. Было бы не медленнее поступить правильно и вызвать delete [], но и быстрее не будет.

Фактически, в MSVC ++ delete [] p немедленно вызывает обычный оператор delete (void * p), когда p является указателем на простой тип или тип без деструкторов.

Те, кто мне не верит, переходят через этот код в код CRT для первых двух вызовов delete [].

#include "stdafx.h"
#include <malloc.h>
#include <iostream>
using namespace std;

class NoDestructor
{
    int m_i;
};

class WithDestructor
{
public:
    ~WithDestructor()
    {
        cout << "deleted one WithDestructor at " << (void *) this<< endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    int **p = new int *[20];
    delete [] p;

    p = (int**) malloc(80);
    free(p);

    NoDestructor *pa = new NoDestructor[20];
    delete [] pa;

    WithDestructor *pb = new WithDestructor[20];
    delete [] pb;

    return 0;
}
person Carlos A. Ibarra    schedule 24.04.2009
comment
На самом деле не имеет значения, верим ли мы вам. Я уверен, что в этом конкретном компиляторе дело обстоит именно так. Настоящая проблема в том, что этого не должно быть. MS может изменить это поведение в любое время. использование delete вместо delete [] для чего-то, что было выделено с помощью new [], просто неверно. - person Evan Teran; 25.04.2009
comment
Хорошо, я не защищаю их смешение. Не смешивайте их. Я просто отвечаю на исходный вопрос о том, будет ли это иметь какие-либо плохие последствия, такие как утечки памяти или снижение производительности. Практически на MSVC ++ для этого типа массива это не оказывает плохого воздействия. - person Carlos A. Ibarra; 25.04.2009
comment
@Thinkcube - вы правы, что отвечаете на правильный вопрос, и да - как мы видим, разницы в производительности в этом случае нет, но код работает случайно. Спасибо - person RnR; 25.04.2009

этот оператор оставит все объекты myClass, на которые указали все указатели в массиве, висящими в памяти. Он также оставляет в памяти массив указателей. Нет никакого способа, который может быть полезен, поскольку он освобождает только 32 бита памяти, а ОС все еще думает, что у вас есть (размер) myClasses и указатели на каждый из них. Это просто пример того, как программист не убирает за собой должным образом.

person Scott M.    schedule 24.04.2009
comment
Следует отметить, что delete [] по-прежнему оставит все объекты myClass зависшими, потому что delete [] таблицы просто избавляется от указателей. Программист еще должен удалить для себя указанные объекты. - person Michael Kohne; 25.04.2009

Обратитесь к разделу [16.11] «Как мне выделить / освободить массив вещей?» и не только в C ++ FAQ Lite,

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.11

Они объясняют, что удаление массива является обязательным при создании массива.

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

person eel ghEEz    schedule 25.04.2009
comment
Я бы сказал, что пункт 16.13 еще более уместен - и, похоже, МОЖЕТ быть случай (не этот, вероятно), когда может потребоваться такое преднамеренное отсутствие [] - это было бы, если бы кто-то не отменял новое и не удалял правильно в первую очередь и задавался вопросом, почему он не вызывается или как его вызвать;) - person RnR; 25.04.2009