Questions About Vectors and Deleting Memory Associated With Them
几个月前我用向量法写了一个程序。我使用clear()成员函数"重置"向量,假设它不仅可以清除元素中的项并重置大小数据成员,而且还可以向堆返回以前与它一起使用的内存。好吧,我偶然发现了一篇关于向量的文章,说这不是从向量中获取内存的正确方法,因为使用clear()不会做到这一点,但需要使用交换方法:
1
| vector<MyClass>().swap(myVector); |
我很好奇为什么我们要调用交换来删除旧的内存?我认为这更像是一种权宜之计,因为我们正在使用交换,但其他事情正在发生。是否调用了析构函数?
最后一个问题是,我现在读到的所有文章都说clear()不会释放内存,即对象被"销毁"。有人能解释这是什么意思吗?我对白话不熟悉。我假设如果一个对象被破坏了,它就被清除了,内存又被返回到堆中,但是这是错误的,"破坏"这个词是指仅仅删除与每个元素相关的位吗?我不确定。任何帮助都将不胜感激。谢谢。
- std::vector::clear()应触发向量中元素的析构函数。对于健全性检查,可以在MyClass对象中放置一个print语句,以查看是否发生了这种情况。
- 你很可能读错了你读的东西。
- 向量在清除它们时保持它们的容量,因为它们假定您要向向量添加新元素。因此,在添加内存时,保持内存在周围比重新分配内存更有效。
- 通常,当处理完一个向量后,您会离开声明它的作用域,然后它将被完全销毁。如果您确实需要重置它,那么可以使用swap方法。
- 您需要区分向量本身使用的内存,以及向量中MyClass对象使用的内存。当您清除向量时,后一个对象将被销毁并回收它们的内存,而不是向量本身(内部只是一个指针数组)。
- @Barmar我知道的大多数向量实现都分配了一个大小为sizeof(T)*capacity()的数组,其中成员直接存储在数组中,而不是指针数组。
- @Danielschepler有几年的历史了,但是stackoverflow.com/a/8036528/1491895说向量元素是在堆中分配的。
- 是的,数组存储是在堆上分配的,但是它的布局就像一个T[]数组(除了不是所有元素都初始化)而不是一个T*[]数组。
- 哦,我明白了。向量的内部数组在堆中,因此可以调整大小。
- @Danielschepler认为布局实际上是强制的,因为向量内容必须能够像C样式数组一样工作。
要回答这个问题,您需要将vector直接分配的内存与通过成员对象间接"拥有"的内存分开。例如,假设MyClass是一个占用1000字节的对象,然后使用std::vector>。然后,如果该向量有10个元素,那么在64位系统上,直接分配的内存通常接近10*8=80字节,而unique_ptr对象间接拥有10*1000=10000字节。
现在,如果您在向量上调用clear(),则在每个unique_ptr元素上调用析构函数,从而释放10000个间接拥有的字节。但是,底层数组存储没有释放,因此向量直接拥有的80+字节仍然被分配。(此时,向量的capacity()至少为10,但size()至少为0。)如果随后调用向量的析构函数,这也将导致释放存储数组。
现在,如果你执行
1
| std::vector<std::unique_ptr<MyClass>>().swap(v); |
让我们来分解它的作用:首先,创建一个临时的向量对象,它没有分配数组,capacity()为0。现在,swap将v的底层数组转移到临时向量,并将空数组或空数组从临时向量交换到v中。最后,在表达式的末尾,临时对象超出了范围,因此调用了其析构函数。这会导致调用以前属于v的任何元素的析构函数,然后释放以前属于v的底层数组存储。因此,在结束时,v.capacity()为0,所有以前属于v的内存都被释放,不管它是由v直接分配的还是通过存储的unique_ptr对象间接属于它。
- 你说的reset()是指clear()吗?
- 当然,纠正了。
- @丹尼尔斯切普勒,你的回答非常具体,非常感谢。我只有一个问题。因此,调用swap函数将有效地释放clear()释放(销毁对象)的相同内存,并释放与向量本身关联的内存,因此,如果用户要执行clear(),那么交换将是多余的,因为交换本身释放两组内存。这是正确的吗?我只是想确保我理解正确。似乎交换在释放内存时覆盖了两个基,而clear只覆盖一个基。
- 是的,没错。
vector有一个关联的量,称为capacity,这意味着它已经为许多元素分配了足够的内存,即使它目前实际上不包含那么多元素。
如果在不超过容量的情况下从向量中添加或移除元素,则不会分配或释放内存;个别元素的构造函数和析构函数将在已分配的空间上运行。
clear()功能不会改变容量。但是,通常的vector::swap()的实现也会交换向量的容量;因此,与空向量交换将导致原始向量具有默认容量,该容量将很小甚至为零(依赖于实现),因此应释放内存。
由于C++ 11有一种减少容量的正式方法,称为SyrStutoFig()。
请注意,C++标准实际上并不要求在减少容量之后将内存释放到OS上,这将取决于您使用的库实现的作者和操作系统的组合。