簡體   English   中英

push_back 是如何在 STL 向量中實現的?

[英]how is push_back implemented in STL vector?

我在一次采訪中被問到這個問題。

我回答的要點是這樣的

1) 指向當前位置的索引;

2) 必要時調整大小。

有人可以詳細說明嗎?

STL vector具有size (當前存儲元素的數量)和capacity (當前分配的存儲空間)。

  • 如果size < capacity ,則push_back只是將新元素放在末尾並將size增加 1。
  • 如果在push_back之前size == capacity ,則分配一個新的更大的數組(大小的兩倍是常見的,但這是依賴於實現的afaik),所有當前數據都被復制過來(包括新元素),並且舊的分配的空間被釋放。 如果分配失敗,這可能會引發異常。

操作的復雜性分攤為O(1),這意味着在導致調整大小的push_back期間,它不會是恆定時間操作(但通常在許多操作中是)。

template< typename T >
void std::vector<T>::push_back(const T& obj)
{
    this->insert(this->end(),obj);
}

當然,這本質上是實現定義的。 假設這是一個關於某人如何實現動態數組的問題,一般來說,它會是這樣的:

  1. push_back檢查capacity()並確保它至少比size() push_back
  2. 如果新元素沒有容量,向量會重新分配它的整個底層存儲,將舊存儲的內容復制到新存儲。 舊存儲被解除分配。
  3. 新元素被復制到動態數組的末尾。

一些 STL 實現將通過使用交換(即對於容器的容器)來消除一些副本,但在大多數情況下,這正是它的工作原理。

可能他們正在尋找的是push_back制作被推送到vector上的對象的副本(使用其復制構造函數)。

關於調整大小:標准說a.push_back(x)等效於a.insert(a.end(),x) insert的定義部分說明:“如果新大小大於舊容量,則會導致重新分配。”

標准說明了函數應該做什么。 但是在大多數情況下,它們的實現方式是特定於實現的。

像這樣:


void push_back(T const& param)
{
  vector temp(rbegin(), rend());
  temp.push_front(param);
  *this = vector(temp.rbegin(), temp.rend());
}

如果 size < capasity,那么一切似乎都沒問題,您只需在 vector 中插入 en 元素,當 size == capasity 時復雜性會增加。

用文字很容易解釋,您必須分配一個比現有數組大兩倍的新數組,並將所有這些元素復制到新分配的內存中,然后刪除舊數組並將新元素插入新數組中。 但這里有一些我認為你的面試官希望你提到的關鍵時刻。

  1. std::vector 中的元素不保存在一個數組中,因此 std::vector[1000] 的數據成員不是長度為 1000 的 int*。元素保存在內存塊中,因此您必須在使用時考慮它復制。

  2. 其次,標准 stl 向量需要“給我一個可復制的對象,我可以插入它”,這意味着對 std::vector 的要求是 sometype 只需要有一個 copy_constructer (而不是 operator = )。 因此,當您分配 sometype 類型的新內存時,您必須考慮 sometyme 可能沒有默認的復制構造函數。 在常見的實現中,這是通過放置 new 運算符來完成的,以避免調用復制構造函數。

感謝一些評論,我正在完全修改一個非常不正確的原始答案。

根據 STL 規范,您的答案是正確的。 該向量被實現為一個動態調整大小的數組:

向量容器被實現為動態數組; 就像常規數組一樣,向量容器將它們的元素存儲在連續的存儲位置,這意味着它們的元素不僅可以使用迭代器訪問,還可以使用指向元素的常規指針的偏移量來訪問。

但與常規數組不同的是,向量中的存儲是自動處理的,允許根據需要擴展和收縮。

vector不使用鏈表。 它使用連續內存。

如果沒有足夠的保留空間, push_back分配一個兩倍於原始vector的新內存塊。 通過這樣做,攤銷的運行時間是 O(1)。

面試官想要多少細節? 例如,他是否正在尋找您深入了解較低級別的細節?

除了通常的按需要調整大小以保留平均語義的 O(1) 之外,需要考慮的一些事項包括但不限於:

  • 異常安全:實現是否保證如果在附加新元素時拋出異常(例如回滾語義),向量的狀態不會被修改? 例如,在分配、復制、插入等過程中可能會拋出異常。
  • 正確使用分配器:實現是否正確使用了vector的分配器而不是普通的基於new的舊分配器(兩者可能相同也可能不同)? 理想情況下,這將由vector的調整大小代碼透明地處理,但是實現肯定會有所不同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM