[英]How does vector not destroy element twice after reducing its size?
出于测试目的,我试图创建自己的向量类,但无法弄清楚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)
,第三个元素被销毁,但向量的容量仍为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)];
不会创建任何对象。
对象的生存期由此放置位置new运算符和对析构函数的显式调用来管理。 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;
pop_back
buffer[size].~T();--size;
。 这是应显式调用析构函数的极少数地方之一。
简单的答案是,vector通常通过使用inplace运算符new和operator delete方法在内部管理构造函数和析构函数调用。
弹出元素后,将立即对其进行销毁,并且在内存仍然可用且可能仍包含一些剩余值的情况下,std :: vector本身知道哪些元素仍需要删除,因此不会再次调用析构函数。
vec.resize(2)
,第三个元素被破坏,但向量的容量仍为3。
是。 capacity
是向量的内部数组可以物理容纳多少个元素。 size
是该数组中实际有效的元素数。 缩小size
根本不影响capacity
。
然后,当
vec
被销毁时,其所有元素,包括已经销毁的元素都应被销毁。
先前已销毁并从数组中删除的第三个元素不会再次销毁。 就像您正在思考的那样,仅破坏size
的元素数目,而不破坏capacity
的元素数目。
std::vector
如何知道他已经销毁了该元素?
它分别跟踪size
和capacity
。 从数组中删除一个元素时,其后的元素将分别在数组中向下移动1个插槽,并且size
会减小。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.