【问题标题】:C++ delete vector, objects, free memoryC++ 删除向量、对象、释放内存
【发布时间】:2012-05-05 18:55:00
【问题描述】:

我对删除 C++ 中的内容完全感到困惑。如果我声明一个对象数组并且如果我使用clear() 成员函数。我可以确定内存被释放了吗?

例如:

tempObject obj1;
tempObject obj2;
vector<tempObject> tempVector;

tempVector.pushback(obj1);
tempVector.pushback(obj2);

我可以安全地调用 clear 来释放所有内存吗?还是我需要遍历才能一一删除?

tempVector.clear();

如果把这个场景改成对象的指针,答案会和上面一样吗?

vector<tempObject> *tempVector;
//push objects....
tempVector->clear();

【问题讨论】:

  • 第二种情况与第一种情况没有什么不同,只是它指向一个向量。我认为应该扩大这个问题,以便您了解内存分配/释放应该如何工作,并且可能还有指针。
  • 这个说在函数堆栈中声明它会在关闭时自动调用析构函数。 stackoverflow.com/questions/3054567/…

标签: c++ vector delete-operator


【解决方案1】:

您可以调用 clear,这将销毁所有对象,但不会释放内存。循环遍历各个元素也无济于事(您甚至建议对对象采取什么操作?)您可以做的是:

vector<tempObject>().swap(tempVector);

这将创建一个没有分配内存的空向量并将其与 tempVector 交换,从而有效地释放内存。

C++11 也有函数shrink_to_fit,你可以在调用 clear() 之后调用它,理论上它会缩小容量以适应大小(现在为 0)。然而,这是一个不具约束力的请求,您的实现可以随意忽略它。

【讨论】:

  • 您好,感谢您的回复。您介意解释上述内容吗?打电话后,我需要打电话清除吗?
  • @dupdupdup:不,您不需要调用 clear。 tempVector 中的对象被转移到空向量中。然后,空向量,因为它是一个无名的临时对象,被销毁,它之前由 tempVector 拥有的内容被销毁并释放内存。
  • 我不是 C++ 专家,介意解释一下这比我先尝试的 tempVector.resize(0) 更好吗?
  • std::vector::resize 不需要释放内存,完全可以保留它以备日后需要。
  • @delnan:我不会这样称呼它更好,但它实现了释放内存的目标,而resize(0) 没有。 (这将与clear() 做同样的事情)
【解决方案2】:

这里有两个不同的东西:

  1. 对象生命周期
  2. 存储时间

例如:

{
    vector<MyObject> v;
    // do some stuff, push some objects onto v
    v.clear(); // 1
    // maybe do some more stuff
} // 2

1,你清除v:这会破坏它存储的所有对象。如果你写了一个,每个都会调用它的析构函数,并且 MyObject 拥有的任何东西现在都被释放了。 但是,vector v 有权保留原始存储空间以备日后使用。

如果您决定在12 之间往里面推一些东西,这样可以节省时间,因为它可以重用旧内存。

2,向量v 超出范围:自1 以来您推入其中的任何对象都将被销毁(就像您再次显式调用clear 一样),但现在底层存储也是已发布(v 不会再重复使用它了)。


如果我更改示例使v 成为指向动态分配向量的指针,您需要显式删除它,因为在2 超出范围的指针不会为您执行此操作。在这种情况下最好使用std::unique_ptr 之类的东西,但如果你不这样做并且v 被泄露,它分配的存储空间也会被泄露。如上,你需要确保v被删除,调用clear是不够的。

【讨论】:

  • “如果您决定推送.. 这样可以节省时间,因为它可以重用旧内存。”是什么意思?为什么要添加新项目时旧记忆很重要?
  • vector 必须在内部管理它存储的对象的存储。创建新向量需要分配新的存储空间,但清除和重用现有向量允许(但不保证)重用其已分配的存储空间。
  • @Useless 为什么总是需要删除指针? “如果你使用了vecPtr = &vec;,并且如果vec是一个自动变量,它使用了自动内存,或者vec是一个静态变量或全局变量,它使用了静态内存,你不应该删除它,它会被自动释放。只有可以删除动态内存。”
【解决方案3】:

vector::clear() 不会释放向量分配的内存来存储对象;它为它持有的对象调用析构函数。

例如,如果vector使用一个数组作为后备存储并且当前包含10个元素,那么调用clear()会调用数组中每个对象的析构函数,但是后备数组不会被释放,因此仍有sizeof(T) * 10 字节分配给向量(至少)。 size() 将为 0,但 size() 返回向量中元素的数量,不一定是后备存储的大小。

至于您的第二个问题,您使用new 分配的任何东西都必须使用delete 解除分配。由于这个原因,您通常不会维护指向向量的指针。很少(如果有的话)有充分的理由这样做,并且您可以防止向量在离开范围时被清理。但是,无论分配方式如何,调用 clear() 仍然会以相同的方式执行。

【讨论】:

    【解决方案4】:

    如果我使用clear() 成员函数。我可以确定内存被释放了吗?

    不,clear() 成员函数会破坏向量中包含的每个对象,但它会保持向量的 capacity 不变。它影响向量的大小,但不影响容量。

    如果要更改向量的容量,可以使用clear-and-minimize idiom,即创建一个(临时)空向量,然后交换两个向量。


    您可以轻松了解每种方法对容量的影响。考虑以下函数模板,它在传递的向量上调用 clear() 成员函数:

    template<typename T>
    auto clear(std::vector<T>& vec) {
       vec.clear();
       return vec.capacity();
    }
    

    现在,考虑函数模板empty_swap(),它将传递的向量与一个空向量交换:

    template<typename T>
    auto empty_swap(std::vector<T>& vec) {
       std::vector<T>().swap(vec);
       return vec.capacity();
    }
    

    两个函数模板都返回向量在返回时刻的容量,则:

    std::vector<double> v(1000), u(1000);
    std::cout << clear(v) << '\n';
    std::cout << empty_swap(u) << '\n';
    

    输出:

    1000
    0
    

    【讨论】:

      【解决方案5】:

      移动语义允许通过简单地从空右值应用assignment (=) operator 来释放内存:

      std::vector<uint32_t> vec(100, 0);
      std::cout << vec.capacity(); // 100
      
      vec = vector<uint32_t>();    // Same as "vector<uint32_t>().swap(vec)";    
      std::cout << vec.capacity(); // 0
      

      它与其他答案中描述的基于“swap()”的方法一样有效(实际上,两者在概念上都在做同样的事情)。然而,就可读性而言,赋值版本在表达程序员意图方面做得更好,同时更简洁。

      【讨论】:

      • 嗨.. 只是一个简单的问题,如果我使用“新”分配内存。我仍然可以使用您的答案来释放内存而不是“删除”吗?
      • 是的,没问题,但请记住:这种方法是释放 'vec' 使用的存储内存,而不破坏 'vec' 本身。对于与此类容器(向量)相关的基本指针和其他本机内容,仍然消耗最少的空间。如果你想在使用'new'后销毁它,那么'delete'是必需的。
      • +1 以提高可读性。令人惊讶的是,“花哨”的 swap() 方法是如何主导搜索结果的……
      【解决方案6】:

      您可以通过这种方式释放vector使用的内存:

      //Removes all elements in vector
      v.clear()
      
      //Frees the memory which is not used by the vector
      v.shrink_to_fit();
      

      【讨论】:

        【解决方案7】:

        如果您需要一遍又一遍地使用向量并且您当前的代码在循环中或每次函数调用中重复声明它,您很可能会耗尽内存。我建议你在外面声明它,将它们作为指针传递给你的函数并使用:

        my_arr.resize()
        

        这样,您可以继续为向量使用相同的内存序列,而不是每次都请求新序列。 希望这有帮助。 注意:将其调整为不同的大小可能会添加随机值。如果需要,传递一个整数,例如 0 来初始化它们。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-11-27
          • 1970-01-01
          • 2017-03-01
          • 2018-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多