[英]How does vector not destroy element twice after reducing its size?
For testing purposes, I was trying to create my own vector class, and I couldn't figure out how std::vector
size reduction works. 出于测试目的,我试图创建自己的向量类,但无法弄清楚
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;
}
Output is 输出是
A constructed (1)
A constructed (2)
A constructed (3)
A destroyed (1)
Vector resized
A destroyed (2)
A destroyed (3)
When vec.resize(2)
is called, the third element is destroyed, but the vector's capacity is still 3. Then when vec
is destroyed, all of its elements including the one already destroyed should be destroyed. 当
vec.resize(2)
,第三个元素被销毁,但向量的容量仍为3。然后,当vec
被销毁时,其所有元素(包括已销毁的元素)都应被销毁。 How does std::vector
know that he has already destroyed that element? std::vector
如何知道他已经销毁了该元素? How can I implement that in my vector class? 如何在向量类中实现它?
There's a difference between capacity and size. 容量和大小之间有区别。 Given a
std::vector<T> v;
给定一个
std::vector<T> v;
The vector has allocated memory for v.capacity()
elements. 该向量已为
v.capacity()
元素分配了内存。 But only in the first v.size()
places contain constructed T
objects. 但仅在第一个
v.size()
位置包含构造的T
对象。
So, v.reserve(1000)
on an empty vector won't call any additional constructors. 因此,在空向量上的
v.reserve(1000)
不会调用任何其他构造函数。 vec.resize(2)
in your example destroys the last element and vec[2]
is now an empty place in memory, but memory still owned by the vec
. 您的示例中的
vec.resize(2)
破坏了最后一个元素,并且vec[2]
现在在内存中为空,但内存仍由vec
拥有。
I think your allocation looks like buffer = new T[newSize];
我认为您的分配看起来像
buffer = new T[newSize];
. 。 That is not how
std::vector
works which would not allow Ts
that do not have default constructors. 这不是
std::vector
工作方式,不允许没有默认构造函数的Ts
。 Maybe you did not realize this, but whenever you obtain a piece of memory, it already contains objects, let it be T x;
也许您没有意识到这一点,但是每当获得一块内存时,它已经包含对象,将其设为
T x;
or even new double[newSize];
甚至是
new double[newSize];
which returns an array of doubles (although their constructors are empty). 它返回一个双精度数组(尽管它们的构造函数为空)。
There is only one way to get usable uninitialized memory in C++, which is to allocate chars
. 在C ++中只有一种获得可用的未初始化内存的方法,即分配
chars
。 This is due to the strict aliasing rule. 这是由于严格的别名规则。 There also (is|must be) a way how to call a constructor explicitly on this memory ie how to create an object there.
还有(必须是)如何在此内存上显式调用构造函数的方法,即如何在该内存上创建对象。 The vector uses something called placement new which does precisely that.
向量使用称为“ 新放置”的东西来精确地做到这一点。 The allocation is then simply
buffer = new char[newSize*sizeof(T)];
然后,分配就是
buffer = new char[newSize*sizeof(T)];
which creates no objects whatsoever. 不会创建任何对象。
The lifetime of the objects is managed by this placement new operator and explicit calls to destructors. 对象的生存期由此放置位置new运算符和对析构函数的显式调用来管理。
emplace_back(arg1,arg2)
could be implemented as {new(buffer + size) T(arg1,arg2);++size;}
. emplace_back(arg1,arg2)
可以实现为{new(buffer + size) T(arg1,arg2);++size;}
。 Note that simply doing buffer[size]=T(arg1,arg2);
请注意,只需执行
buffer[size]=T(arg1,arg2);
is incorrect and UB. 和UB不正确。
operator=
expects that the left size( *this
) already exists. operator=
期望左边的大小( *this
)已经存在。
If you want to destroy an object with eg pop_back
, you must do buffer[size].~T();--size;
如果要使用
pop_back
破坏对象,则必须执行buffer[size].~T();--size;
pop_back
buffer[size].~T();--size;
. 。 This is one of the very few places where you should call the destructor explicitly.
这是应显式调用析构函数的极少数地方之一。
The simple answer is that vector internally manages constructor and destructor calls, often by using inplace operator new and operator delete methods. 简单的答案是,vector通常通过使用inplace运算符new和operator delete方法在内部管理构造函数和析构函数调用。
Having popped an element, it is immediately descructed, and while the memory is still available, and may still contain some remnant values, std::vector itself knows which elements still need to be deleted, and won't call the destructor again. 弹出元素后,将立即对其进行销毁,并且在内存仍然可用且可能仍包含一些剩余值的情况下,std :: vector本身知道哪些元素仍需要删除,因此不会再次调用析构函数。
When
vec.resize(2)
is called, the third element is destroyed, but the vector's capacity is still 3.vec.resize(2)
,第三个元素被破坏,但向量的容量仍为3。
Yes. 是。 The
capacity
is how many elements the vector's internal array can physically hold. capacity
是向量的内部数组可以物理容纳多少个元素。 The size
is how many elements in that array are actually valid. size
是该数组中实际有效的元素数。 Shrinking the size
does not affect the capacity
at all. 缩小
size
根本不影响capacity
。
Then when
vec
is destroyed, all of its elements including the one already destroyed should be destroyed.然后,当
vec
被销毁时,其所有元素,包括已经销毁的元素都应被销毁。
The 3rd element that was previously destroyed and removed from the array is not destroyed again. 先前已销毁并从数组中删除的第三个元素不会再次销毁。 Only
size
number of elements are destroyed, not capacity
number of elements, like you are thinking. 就像您正在思考的那样,仅破坏
size
的元素数目,而不破坏capacity
的元素数目。
How does
std::vector
know that he has already destroyed that element?std::vector
如何知道他已经销毁了该元素?
It tracks the size
and capacity
separately. 它分别跟踪
size
和capacity
。 When an element is removed from the array, the elements that follow it are moved down the array by 1 slot each, and the size
is decremented. 从数组中删除一个元素时,其后的元素将分别在数组中向下移动1个插槽,并且
size
会减小。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.