Зачем ты пишешь что-то подобное? (намеренно не использовать delete [] для массива) - PullRequest
7 голосов
/ 25 апреля 2009

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

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

Ответы [ 8 ]

19 голосов
/ 25 апреля 2009

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

8 голосов
/ 25 апреля 2009

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

8 голосов
/ 25 апреля 2009

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

5 голосов
/ 25 апреля 2009

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

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

4 голосов
/ 27 апреля 2009

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

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

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

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

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

2 голосов
/ 25 апреля 2009

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

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

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

Однако, как отмечали другие, неправильно смешивать в C ++ new [] и удалять без []. Хотя в этом случае он может работать в 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;
}
1 голос
/ 25 апреля 2009

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

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

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

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

1 голос
/ 25 апреля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...