[英]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.