释放分配给存储在向量内的堆的内存

时间:2014-10-15 18:08:25

标签: c++ memory-management vector

我通过将它的内存分配给堆来创建了一个向量。然后我创建10个字符串对象也分配给堆内存并将它们存储在向量中。我试图使用delete运算符释放与每个新字符串对象关联的内存,但我不是如何做到的。我正在使用C ++ 11。

#include <vector>
#include <string>
#include <iostream>

using namespace std;

int main()
{
   vector<string> *v = new vector<string>;

   for(int i = 0; i < 10; i++) {
     // allocate a new string object on the heap
     string *a = new string("Hello World");
     //de-reference the string object
     v->push_back(*a);
   }

  // show the contents of the vector
  for(auto i = v->begin(); i != v->end(); ++i) {
    // okay so this makes a lot more sense than:
    // const string &s = *i;
    // this way we create a pointer to a string object
    // it is a lot more clear this way
    const string *s = &(*i);
    cout << *s << " " << s->length() << endl;
  }

  cout << endl << endl;

  for(vector<string>::iterator it = v->begin(); it != v->end(); ++it) {
    delete &it;
    v->erase(it);
  }

  for(auto i = v->begin(); i != v->end(); ++i) {
   cout << *i << endl;
  }
  cout << endl << "Size: " << v->size() << endl;

  delete v;
}

g++ -std=c++11 main.cc -o main

我的错误是并非所有对象都被删除。我最后在最后4个语句后留下了5个对象。一旦完成这些操作,我期望向量内部有零个对象。

我的输出:

Hello World 11
Hello World 11
Hello World 11
Hello World 11
Hello World 11
Hello World 11
Hello World 11
Hello World 11
Hello World 11
Hello World 11


Hello World
Hello World
Hello World
Hello World
Hello World
Size: 5

问题是并非所有对象都被删除。

4 个答案:

答案 0 :(得分:5)

我认为你在无数问题中担心的特殊问题是你的for循环并没有删除所有项目,即:

for(vector<string>::iterator it = v->begin(); it != v->end(); ++it) {
  delete &it;
  v->erase(it);
}

您的问题是您正在更改正在迭代的向量,这会导致未定义的行为,因为您没有删除所有值。 (只要不添加或删除值,向量的迭代器才有效。)

可以解释为什么它只做5,但答案不是跨平台的。在这种情况下,编译器可以随意做任何事情。对于编译器来说,让恶魔飞出你的鼻子也同样有效。

基本上,你正在删除一个值,然后他们转移到下一个索引。所以你在0处删除了什么,它会将1处的值拉到0处。然后你移动到索引1,它包含以前在索引2中的内容。然后你删除它。基本上,您从向量中删除所有偶数索引。

编辑:将问题减少到可重复的最小值:

std::vector<int> vals;

for (int i = 0; i < 11; i++0) vals.push_back(i);

for (std::vector<int>::iterator i = vals.begin(); i != vals.end(); ++i)
{
   vals.erase(i);//after this point, i's behavior is undefined!
}

编辑3:列举所有代码问题(基于元对话的建议操作)

  1. Vector已经在堆中分配了它的集合内存(除了指向内存和大小计数器的指针之外的所有内容)。整个方法基于不理解这一事实。新的向量确实将整个向量放在堆中,这可能是OP想要的。
  2. 字符串也在堆中分配其内存。 new std::string将简单地将指针分配给字符数组和堆中的大小。所以这也值得注意。
  3. 如果你想要一个指针矢量,你应该有std::vector<T*>std::vector<T>将是T
  4. 的实例向量
  5. 由于向量的内存已经在堆上分配,这最初似乎是徒劳的努力。 但是,在某些情况下,您可能需要这样的结构。例如,必须以这种方式存储多态类型以避免切片。 (但我会使用智能指针,但出于教育目的,这是一个很好的练习)。
  6. 在循环1中
  7. ,您可以动态分配字符串。然后,使用复制构造函数在向量中实例化实体实例。然后让指针超出范围。这是内存泄漏和低效副本。
  8. 您似乎真的想使用指针作为参考。 std::string& s = *it比获取地址更具可读性。
  9. juanchopanza正确地指出你正在删除迭代器的位置,这是更加未定义的行为。 &it的类型为std::vector<string>::iterator*。为什么这可能不会崩溃和烧毁的是迭代器类型只包含内置类型,并且在此之后你没有做太多(你可能已经为自己设置了一些美味的堆栈损坏)。如果你有一个std::vector<T*>(匹配项目1),并且该向量具有唯一所有权,那么你需要这样做来清理它:
  10. delete &(*it);

    但是做std::vector<std::unique_ptr<T>>这样的事情会好得多,而且从不担心。

    1. 永远不要使用全局使用标准。

答案 1 :(得分:2)

  

问题是并非所有对象都被删除。

这是因为当你填充向量时你正在泄漏字符串。

for(int i = 0; i < 10; i++) {
  string *a = new string("Hello World"); // Leak: who deletes a? Nobody!
  v->push_back(*a);
}

这是一种避免特定泄漏的方法:

for(int i = 0; i < 10; i++) {
  v->push_back("Hello World");
}

但是,您正在动态分配v,并且没有理由这样做。在C ++中,这将是一种更简单和惯用的方法。与您的版本不同,它不涉及内存泄漏或未定义的行为:

int main()
{
   vector<string> v(10, "Hello World");

   for (auto& s : v)
     cout << s.length() << endl;
}

你不需要指针。您无需致电new。您无需担心内存管理问题。

答案 2 :(得分:1)

当您使用v-&gt; push_back(* a)时,您正在向量上推送字符串的副本;命令。如果你不想这样做,你需要让你的向量成为字符串指针的向量,然后将指针自身推到向量上。

答案 3 :(得分:0)

我不明白你为什么要动态分配字符串,即使你是按值存储它。这个片段即使是教育目的也没有意义,除非你学习如何在C ++中泄漏内存。

   for(int i = 0; i < 10; i++) {
     // allocate a new string object on the heap
     string *a = new string("Hello World");
     //de-reference the string object
     v->push_back(*a);
   }

如果你想在任何stl容器中存储指针,那么我建议使用一些智能指针(EXCLUDING AUTO_PTR),如boost :: shared_ptr。他们会为您处理内存管理。