矢量减小元素的大小后,如何不会破坏元素两次

How does vector not destroy element twice after reducing its size?

本文关键字:元素 两次 何不会      更新时间:2023-10-16

出于测试目的,我试图创建自己的向量类,而我无法弄清楚 std::vector size younk yougn fors。

class A
{
    A()
    { std::cout << "A constructed") << std::endl; }
    ~A()
    { std::cout << "A destroyed") << std::endl; }
}
main()
{
    std::vector<A> vec(3, A());
    vec.resize(2);
    std::cout << "vector resized" << std::endl;
}

输出是

A constructed       (1)
A constructed       (2)
A constructed       (3)
A destroyed         (1)
Vector resized
A destroyed         (2)
A destroyed         (3)

当调用vec.resize(2)时,第三个要素被销毁,但向量的容量仍然为3。然后,当vec被销毁时,其所有元素包括已经被销毁的元素都应被销毁。std::vector如何知道他已经破坏了该元素?如何在我的矢量类中实现?

容量和大小之间存在差异。给定std::vector<T> v;,向量已为v.capacity()元素分配了内存。但是只有在第一个v.size()的位置中包含构造的T对象。

因此,空矢量上的v.reserve(1000)不会调用任何其他构造函数。vec.resize(2)在您的示例中破坏了最后一个元素,而vec[2]现在是内存中空的位置,但存储器仍然由vec拥有。

我认为您的分配看起来像buffer = new T[newSize];。这不是std::vector的工作方式,不允许没有默认构造函数的Ts。也许您没有意识到这一点,但是每当您获得一块内存时,它已经包含对象,让它为T x;,甚至是new double[newSize];,它返回了双打阵列(尽管它们的构造函数是空的(。

只有一种方法可以在C 中获得可用的非初始化内存,即分配chars。这是由于严格的别名规则。也(必须是((必须是(在此内存上明确调用构造函数的一种方法,即如何在此处创建对象。矢量使用了所谓的新位置,这正是这样做的。然后,分配是简单的buffer = new char[newSize*sizeof(T)];,它不会创建任何对象。

对象的寿命由此放置的新操作员管理,并向驱动器进行明确调用。 emplace_back(arg1,arg2)可以用作{new(buffer + size) T(arg1,arg2);++size;}。请注意,简单地执行buffer[size]=T(arg1,arg2);是不正确的,并且UB。operator=期望左大小(*this(已经存在。

如果您想用例如pop_back,您必须做 buffer[size].~T();--size;。这是您应该明确称呼驱动器的几个地方之一。

简单的答案是向量内部管理构造函数和破坏者调用,通常是通过使用Inplace Operator New and Operator删除方法。

弹出元素后,立即进行了描述,并且在存储器仍然可用,并且可能仍包含一些残余值,std :: vector本身知道仍然需要删除哪些元素,并且不会调用destructor再次。

当调用vec.resize(2)时,第三个元素被销毁,但向量的容量仍然为3。

是。capacity是向量的内部数组可以物理上保持多少元素。size是该数组中有多少元素实际上有效。收缩size完全不会影响capacity

然后,当vec被销毁时,其所有元素包括已经被销毁的元素都应被销毁。

以前被摧毁并从数组中删除的第三个元素不会再次被摧毁。只有size元素数量被破坏,而不是capacity元素数量,就像您在想的那样。

std::vector如何知道他已经破坏了该元素?

它分别跟踪sizecapacity。当从数组中删除一个元素时,随后的元素将其向下移动数组,每个插槽将size降低。