vector如何在减小尺寸后不破坏元素两次?

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

出于测试目的,我试图创建自己的向量 class,但我无法弄清楚 std::vector 尺寸缩减的工作原理。

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)时,第三个元素被销毁,但vector的容量仍然是3。那么当vec被销毁时,它的所有元素包括已经被销毁的元素都应该被销毁. std::vector怎么知道他已经破坏了那个元素?我怎样才能在我的向量中实现它 class?

容量和尺寸之间存在差异。给定一个 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]; 其中 returns 一个双精度数组(尽管它们的构造函数是空的)。

在 C++ 中只有一种方法可以获得可用的未初始化内存,即分配 chars。这是由于 strict aliasing rule. There also (is|must be) a way how to call a constructor explicitly on this memory i.e. how to create an object there. The vector uses something called placement new 正是这样做的。然后分配只是 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;。这是您应该显式调用析构函数的极少数地方之一。

简单的答案是 vector 在内部管理构造函数和析构函数调用,通常通过使用 inplace operator new 和 operator delete 方法。

弹出一个元素后,它立即被分解,虽然内存仍然可用,并且可能仍然包含一些剩余值,但std::vector本身知道哪些元素仍然需要删除,并且不会再次调用析构函数。

When vec.resize(2) is called, the third element is destroyed, but the vector's capacity is still 3.

是的。 capacity 是向量的内部数组在物理上可以容纳多少个元素。 size 是该数组中实际有效的元素数量。缩小 size 根本不会影响 capacity

Then when vec is destroyed, all of its elements including the one already destroyed should be destroyed.

之前被销毁并从数组中移除的第三个元素不会再次被销毁。只有 size 个元素被破坏,而不是 capacity 个元素,就像你想的那样。

How does std::vector know that he has already destroyed that element?

它分别跟踪sizecapacity。当一个元素从数组中移除时,它后面的元素每个都在数组中向下移动 1 个槽位,并且 size 递减。