C++删除向量、对象、释放内存

73

我对在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();

1
除了指向向量的事实之外,第二种情况与第一种情况没有任何不同。我认为这个问题应该扩大范围,以便您了解内存分配/释放应该如何工作,可能还涉及指针。 - Marlon
这段代码表明在函数栈内声明它会自动调用闭包的析构函数。https://dev59.com/I3A75IYBdhLWcg3w4tR7 - Udayraj Deshmukh
7个回答

109

你可以调用clear方法,它会销毁所有对象,但不会释放内存。遍历每个元素也无法帮助你(你甚至要对这些对象采取什么操作呢?)你可以这样做:

vector<tempObject>().swap(tempVector);
那会创建一个没有分配内存的空向量,并将其与tempVector交换,有效地释放内存。
C++11还有一个函数`shrink_to_fit`,在调用`clear()`后可以调用它,理论上它会将容量缩小到适合大小(现在为0)。但是这是一个非约束性请求,你的实现可以选择忽略它。

5
不需要调用clear函数。之前在tempVector里的对象被转移到空向量中。然后,由于空向量是一个无名临时对象,它会被销毁,而它的内容,之前由tempVector拥有,也会被销毁并释放内存。 - Benjamin Lindley
2
std::vector::resize 不需要释放内存,如果以后需要使用,完全可以保留它。 - Useless
1
@delnan:我不会说它更好,但它实现了释放内存的目标,而resize(0)则没有。(那将做与clear()相同的事情) - Benjamin Lindley
我认为resizeclear都是以_元素数量_的方式描述的,除了需要足够的底层存储来满足请求的元素数量之外,没有明确规定任何要求。 - Useless
简单的赋值操作不可以吗?tempVector = vector<tempObject>(); - matthias_buehlmann
显示剩余3条评论

34

这里有两个不同的概念:

  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拥有的东西现在都被释放了。然而,如果您以后还需要它,向量v有权保留原始存储空间。

如果您决定在12之间再推入一些内容,则可以节省时间,因为它可以重复使用旧内存。

2处,向量v超出范围:您推入其中的任何对象(就像您明确调用了clear一样)都将被销毁,但现在底层存储也被释放了(v不再可用于重用它)。


如果我更改示例,使v成为指向动态分配向量的指针,则需要显式删除它,因为在2处离开作用域的指针不会为您执行此操作。在这种情况下最好使用std::unique_ptr,但如果您没有这样做,并且v泄漏了,则分配的存储空间也将泄漏。如上所述,您需要确保删除v,而调用clear是不够的。


“如果你决定推送...这可以节省时间,因为它可以重复使用旧内存。” 这句话的意思是什么?当你想添加新项目时,为什么旧内存很重要? - user13107
向量必须在内部管理其存储的对象。创建新向量需要分配新存储空间,但清除并重用现有向量允许(但不保证)重用其已分配的存储空间。 - Useless
@Useless 为什么你总是需要删除指针呢?"如果你使用了 vecPtr = &vec;,并且 vec 是一个自动变量,它使用了自动内存,或者 vec 是一个静态变量或全局变量,它使用了静态内存,那么你不应该删除它,它会自动被释放。只有动态内存才能被删除。" - user5927645

20

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

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

至于您的第二个问题,您使用new分配的任何内容都必须使用delete释放。出于这个原因,您通常不会维护指向向量的指针。很少(如果有)有一个好的理由这样做,并且当离开作用域时,您会防止向量被清除。但是,无论如何分配,调用clear()仍将具有相同的效果。


9
移动语义允许通过简单地将空的右值应用于赋值(=)运算符来释放内存。
std::vector<int> vec(100, 0);
std::cout << vec.capacity(); // 100

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

它与其他答案中描述的基于 "swap() " 的方法一样有效(实际上,两者在概念上都在做同样的事情)。然而,就可读性而言,赋值版本更好。


嗨..只是一个快速的问题,如果我使用“new”分配内存。我是否仍然可以使用您的答案来释放内存,而不是使用“delete”呢? - Gow.
1
令人惊讶的是,“花式” swap() 方法主导了搜索结果,这对于编程来说非常值得注意。 - fchen
1
感谢这种方法的易读性,比起交换方法要好一些。+1 - Morc

7
如果我使用clear()成员函数,我能确定内存已被释放吗?不,clear()成员函数会销毁向量中包含的每个对象,但它并不会改变向量的容量capacity。它会影响向量的大小,但不会影响容量。如果您想改变向量的容量,可以使用清空和最小化惯用语,即创建一个(临时)空向量,然后交换两个向量。
你可以轻松地看到每种方法如何影响容量。考虑以下函数模板,它调用传递的向量上的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

2
您可以通过以下方式释放向量使用的内存:
//Removes all elements in vector
v.clear()

//Frees the memory which is not used by the vector
v.shrink_to_fit();

0
如果您需要多次使用向量,并且当前代码在循环内或每个函数调用中重复声明它,那么很可能会耗尽内存。我建议您在外部声明它们,将它们作为指针传递给您的函数并使用以下代码:
my_arr.resize()

这样,您可以继续使用相同的内存序列来管理您的向量,而不是每次都请求新的序列。

希望这有所帮助。

注意:调整大小可能会添加随机值。如果需要,传递整数(例如0)进行初始化。


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