Как правильно освободить std :: vector указателей в C ++? - PullRequest
16 голосов
/ 27 июля 2010

Я искал StackOverflow, но не смог найти ответ на этот вопрос.

Предположим, у меня есть std::vector<Day *> vector_day, то есть вектор указателей на Day объект.Теперь у меня push_back до vector_day много элементов:

vector_day.push_back(new Day(12));
vector_day.push_back(new Day(99));
vector_day.push_back(new Day(71));
...

Теперь в какой-то момент мне больше не нужен vector_day.Как правильно освободить память?

Это правильный путь:

for (std::vector<Day *>::iterator i = vector_day.begin(); i != vector_day.end(); ++i) {
    delete *i;
}

Разве это не делает недействительным вектор при каждом удалении?Я очень смущен.

Ответы [ 10 ]

17 голосов
/ 27 июля 2010

Лучший способ - не помещать указатели в вектор, если вам это абсолютно не нужно.

Но если вам действительно нужно иметь вектор указателей, то то, как вы это делаете, прекрасно (но .clear() вектор после слов, если он не будет немедленно уничтожен, так что он не полон висячие указатели)

Заявление

delete *it;

не влияет на итератор. Он не изменяет итератор, не делает его недействительным и не удаляет указатель, на который ссылается итератор, из коллекции. Все, что он делает, это освобождает память, на которую указывает указатель, на который указывает итератор. Сам указатель должен быть удален из коллекции отдельно.

7 голосов
/ 27 июля 2010

Повышение ptr_vector на помощь!

Делает именно то, что вам нужно, без необходимости повторять и удалять содержимое std :: vector

4 голосов
/ 27 июля 2010

Другой способ сделать это на C ++ - определить вспомогательную структуру:

struct delete_ptr { // Helper function to ease cleanup of container
    template <typename P>
    void operator () (P p) {
        delete p;
    }
};

, а затем использовать алгоритмы:

std::for_each(vector_day.begin(), vector_day.end(), delete_ptr());
vector_day.clear();
2 голосов
/ 27 июля 2010

В целом в C ++ вы должны как можно больше скрывать управление памятью, чтобы избежать ошибок памяти. Если вы не копируете указатели и не заботитесь о производительности, я бы просто использовал shared_ptr.

Он является частью стандарта TR1 и доступен в большинстве современных компиляторов C ++ «из коробки» (http://anteru.net/2008/09/01/260/)) и отлично подходит для управления памятью и запуска памяти.

1 голос
/ 27 июля 2010

Возможно, вам следует использовать какой-то управляемый указатель, скорее всего, общий указатель.

Если вы удалите вектор, пока кто-то еще удерживает один из этих указателей, вы получите очень неприятное поведение, если попытаетесь разыменовать его. Общий указатель избавит вас от этой головной боли.

Если вы можете гарантировать, что ничто иное не будет ссылаться на указатели после удаления вектора, тогда вы все равно сможете воспользоваться автоматическим указателем. Он будет управлять освобождением для вас, когда вектор будет уничтожен. Накладные расходы минимальны, и это делает вашу жизнь намного проще.

0 голосов
/ 02 июля 2018

Другой способ итерации + удаления - использование цикла while(!empty).Преимущество этого метода состоит в том, что элемент сначала удаляется из контейнера, а затем удаляется после слов.Это безопасно для любого контейнера:

while (!vector_day.empty()) {
    Day* day = vector_day.back();
    vector_day.pop_back();
    delete day;
}
0 голосов
/ 28 июля 2010

Вот удобный класс, который я написал некоторое время назад, имея дело с той же проблемой.Я конвертировал некоторый код из старых векторов и списков на основе RogueWave в векторы и списки на основе STL, и мне нужен был какой-то способ эмулировать метод clearAndDestroy () RW для списков указателей.Метод clearAndDestroy () может быть переопределен для обработки различных типов структур (для краткости я добавил только вектор).

class StlUtils
{
   public:

      /**
       * This method provides a templated way to destroy a std::vector
       * full of pointers.  It is basically a replacement for the RW
       * vector class' clearAndDestroy methods.  The list argument is
       * returned empty.
       *
       * @param list the list of pointers to be destroyed.
       */
      template<class T> static void clearAndDestroy(
         std::vector<T*> &itemList)
      {
         for_each(itemList.begin(), itemList.end(),
                  stl_deleter<T>());
         itemList.clear();
      }

   private:

      /**
       * Templated member function for use with the clearAndDestroy()
       * method.  It provides the method needed by for_each to do the
       * actual deletion.
       */
      template<class T> struct stl_deleter
      {
         void operator() (T* x) {
            if (x != NULL)
               delete x;
         }
      };
};
0 голосов
/ 27 июля 2010

Просто отлично. Вы удаляете *i (объект, указанный элементом вектора), а не i (элемент вектора), поэтому вектор не становится недействительным.

См. этот вопрос для случая, когда разработчик также хотел удалить все i s, и для решения для этого (vector_day.clear()) после цикла.

0 голосов
/ 27 июля 2010

Прежде всего, вы переключились с i на it, но я предполагаю, что это просто опечатка.

Но ответить на ваш квест нет, это нормально.Вы не меняете it, вы меняете *it.

0 голосов
/ 27 июля 2010

Операции, которые добавляют или удаляют элементы из массива, могут сделать недействительными итераторы, проверьте документацию для конкретных правил для различных типов контейнеров.С delete вы воздействуете на данные, содержащиеся в элементах массива, а не на форму массива.Итераторы пересекают форму контейнера, им нет дела до его содержимого.

...