Массив структур и новых / удалить - PullRequest
7 голосов
/ 12 января 2009

У меня есть такая структура:

class Items 
{
private:
    struct item
    {
        unsigned int a, b, c;
    };
    item* items[MAX_ITEMS];
}

Скажем, я хотел «удалить» элемент, например:

items[5] = NULL;

И я создал новый предмет на том же месте позже:

items[5] = new item;

Нужно ли мне звонить delete[], чтобы это убрать? Или это не понадобится, поскольку границы массива items[] известны до компиляции?

Действительно ли указатель на NULL действителен или я должен вызывать delete там?

Ответы [ 8 ]

15 голосов
/ 12 января 2009

Вам нужно позвонить delete, прежде чем установить его в NULL. (Установка его в NULL не требуется, это только помогает уменьшить количество ошибок, если вы случайно попытаетесь разыменовать указатель после его удаления.)

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

Также, new [] и delete [] идут вместе одинаково, но вы никогда не должны смешивать new [] с delete или new с delete []. В вашем примере, поскольку вы создали объект с помощью new (вместо new [], который будет создавать массив объектов), вы должны удалить объект с помощью delete (вместо delete []).

6 голосов
/ 12 января 2009

Как указывал Клюге, вы будете пропускать объект с индексом 5 таким образом. Но это действительно звучит так, как будто вы не должны делать это вручную, а используете класс контейнера внутри Item. Если вам на самом деле не нужно хранить эти item объекты в качестве указателей, используйте std::vector<item> вместо этого массива MAX_ITEMS указателей. Вы всегда можете вставить или стереть векторные элементы в середине, если вам нужно.

Если вам нужно сохранить объекты как указатели (обычно, если struct item действительно полиморфна, в отличие от вашего примера), вы можете использовать boost :: ptr_vector из Boost.PtrContainer вместо этого.

Пример:

class Items {
private:
    struct item {
        unsigned int a, b, c;
    };
    std::vector<item> items;
}

if (items.size() > 5) // (just to ensure there is an element at that position)
    items.erase(items.begin() + 5); // no need to use operator delete at all
1 голос
/ 12 января 2009

Здесь есть несколько связанных вопросов:

  1. В соответствии с кодом, который вы разместили, сам массив не размещается в куче, если только struct не равен, поэтому вам не нужно delete[] массив. Если вы создали массив с new[], вам бы пришлось delete[] его.
  2. В опубликованном коде не указано, как распределяются объекты, на которые указывают массивы. Если вы размещаете эти объекты в стеке, вы не должны удалять их (опять же, это очень маловероятно, поскольку ваши указатели станут недействительными, когда объекты, на которые они указывают, выпадают из области видимости). Если вы разместили их в куче (с новым), то вы должны удалить их, когда они выпадают из области видимости.
  3. Как уже говорили другие, жизнь намного проще, если вы используете контейнер - особенно контейнер STL - и умные указатели - что на данный момент означает указатели из Boost.
1 голос
/ 12 января 2009

Просто чтобы прояснить: вы обращаетесь к звонку "delete[]". Я думаю, что вы имеете в виду delete.

Я упоминаю об этом, потому что в C ++ есть два отдельных оператора, operator delete и operator delete[]. Последний используется для удаления массивов объектов, выделенных с помощью operator new[], и не применяется в этом случае. У вас есть массив указателей на объектов, которые вы должны инициализировать повторными вызовами operator new, а не одним вызовом operator new[].

Все, что я действительно пытаюсь сказать, это: ваше использование delete[] сбивает с толку и неоднозначно; измените его на delete.

1 голос
/ 12 января 2009

Скажем, я хотел «удалить» элемент, например:

пунктов [5] = NULL;

Я немного знаю Visual Basic, но он пахнет языком программирования Visual Basic, поскольку «Set a = None» (или Null, я не уверен) удалит объект, на который указывает (или, скорее, уменьшит его счетчик ссылок , для объектов COM).


Как кто-то еще заметил, вы должны использовать либо:

delete items[5];
items[5] = newContent;

или

delete items[5];
items[5] = NULL;

После delete[5] единственное возможное использование указателя, хранящегося в items[5], вызывает у вас проблемы. Хуже всего то, что это может сработать в начале и начать давать сбои только тогда, когда вы выделите что-то еще в пространстве, ранее использовавшемся *items[5]. Именно эти причины делают программирование на C / C ++ «интересным», то есть действительно раздражающим (даже для тех, кто любит C, таких как я).

Запись только delete items[5]; сохраняет то, что может быть бесполезной записью, но это преждевременная оптимизация.

1 голос
/ 12 января 2009

Чтобы удалить элемент, используйте:

удалить элементы [5];

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

предметов [5] = NULL

0 голосов
/ 12 января 2009

Установка элементов [5] в NULL не удаляет память, связанную с элементом, она просто устанавливает указатель на этот элемент в NULL, поэтому память просачивается.

Вы можете удалить товар, позвонив по телефону:

delete items[5];

Поскольку в C ++ нет автоматической сборки мусора, вам нужно удалить любую память, которая вам больше не нужна.

0 голосов
/ 12 января 2009

C ++ - не моя сильная сторона, но я уверен, что вы потеряете память, если установите указатель на NULL.

РЕДАКТИРОВАТЬ: Утечка памяти будет указывать в массиве указатель памяти.

...