在C++中,释放指向std::vector的指针有哪些正确的方式?

17

我在 StackOverflow 上搜索了这个问题的答案,但没有找到。

假设我有一个 std::vector<Day *> vector_day——也就是指向 Day 对象的指针的向量。现在我要向 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;
}

这样做不会使得每次删除的向量都无效吗?我很困惑。


可能是一个重复的问题,参考 https://dev59.com/I3A75IYBdhLWcg3w4tR7。 - user180100
RC - 不是的,这完全不同。 - bodacydo
1
不要像那样放置指针。如果在向量中放置数据和删除数据之间抛出异常,会怎样呢?你会跳过它并泄漏内存。使用智能指针或指针容器,永远不要用裸指针。 - GManNickG
10个回答

19

如果不是必须的话,最好一开始就不要把指针放入向量中。

但如果确实需要一个指针向量,那么你现在的方法是可以的(但如果该向量不会立即被销毁,那么在使用后要.clear()该向量,以免出现悬空指针)。

这个语句的意思是:

delete *it;

对迭代器没有影响。它不会改变迭代器,使迭代器失效,也不会从集合中移除迭代器所引用的指针。它所做的只是释放迭代器所指向的指针所占用的内存。指针本身必须单独从集合中移除。


谢谢您的解释。所以当向量超出作用域时,指针会自动删除,是这样吗? - bodacydo
1
@bodacydo:没错。如果你在vector即将超出作用域之前正确地进行了delete *it调用,就不必担心调用clear() - Justin Ardini

7

Boost ptr_vector 来拯救你!

它能够满足你的需求,而且不需要遍历和删除 std::vector 的内容。


我需要将一个 vector<T*> 传递给一个函数。ptr_vector<T> 可以转换为 vector<T*> 吗? - Eyal

5

另一种使用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

一般来说,在C++中,为了避免内存错误,应尽可能隐藏内存管理。除非您需要大量复制指针并且非常关心性能,否则我建议您只使用shared_ptr。

它是TR1标准的一部分,并且在大多数现代C++编译器中可以直接使用 (http://anteru.net/2008/09/01/260/) ,非常适合快速、轻松地进行内存管理。


1

你应该使用某种管理指针,最可能是共享指针。

如果在其他人仍然持有其中一个指针的情况下删除向量,如果他们尝试取消引用它,你将会得到一些非常恶劣的行为。共享指针将为您解决这个问题。

如果您可以保证在删除向量后没有其他东西引用指针,则仍然可以从使用自动指针中受益。它将在向量被销毁时为您管理释放。开销很小,可以让您的生活变得更轻松。


0

另一种迭代和删除的方法是使用while(!empty)循环。 这种技术的优点是首先从容器中删除元素,然后再进行删除。这对于任何容器都是安全的:

while (!vector_day.empty()) {
    Day* day = vector_day.back();
    vector_day.pop_back();
    delete day;
}

0

这是我一段时间前写的一个方便的类,当时我正在处理同样的问题。我正在将一些基于旧RogueWave向量和列表的代码转换为基于STL向量和列表,并需要一种方法来模拟RW的clearAndDestroy()方法以用于指针列表。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;
         }
      };
};

您可以删除空指针(这不会有任何作用),因此if语句实际上并不需要。 - ollb

0

对数组进行添加或删除元素的操作可能会使迭代器失效,请查阅不同容器类型的文档以获取具体规则。使用delete时,你是在操作数组元素中包含的数据,而不是数组的形状。迭代器遍历容器的形状,它们并不关心其内容。


我现在更好地理解了失效。感谢您的回答! - bodacydo

0
首先,你从i改成了it,但我认为那只是一个打字错误。
但是回答你的问题,不,那没问题。你没有改变it,而是改变了*it

我纠正了“it”被用于“i”的错误。感谢你发现了它。如果我在循环中执行“i = 0xAABBCCDD”,那显然会更改“i”而不是“it”,这会使向量失效吗? - bodacydo

0

没问题。你删除的是*i(向量元素指向的对象),而不是i(向量元素本身),所以向量并没有失效。

请参见这个问题,其中开发人员还想删除所有的i,并提供了解决方案(循环后使用vector_day.clear())。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接