STD Vector 在 C++ 中是非常有用的工具,但同時也容易被誤用。本文將探討 STD Vector 的常見錯誤使用方式,並提供改進建議,以提升程式碼效能。
什麼是 STD Vector?
STD Vector 是 C++ 標準模板庫(STL)中的一種容器類型。它是一個動態調整大小的陣列,元素在記憶體中連續儲存。這使其兼具陣列的效率和動態調整大小的靈活性。
為何稱之為 Vector?
關於 Vector 這個名稱的由來,可能是設計者 Alex 隨意決定的,後來可能也後悔了。這也是 C++ 常見的情況。
效能考量
使用 STD Vector 的標準
本文主要關注如何提升 STD Vector 的效能。雖然 STL 並非專為極致效能而設計,但透過適當的使用方式,仍然可以大幅提升其效能。
STL 並非永遠最佳選擇
如果效能是首要考量,可能需要考慮其他替代方案,例如自行設計資料結構,或使用專為效能優化的函式庫,例如 EA 的 EASTL。EASTL 是為遊戲開發優化的 STL 版本,更注重速度和效能。
何時不該使用 STD Vector?
評估是否需要動態大小
使用 STD Vector 的首要原則是評估是否真的需要它。若能使用固定大小的陣列,則應優先考慮 std::array
。
Stack vs. Heap
std::array
儲存在堆疊 (Stack) 中,而 STD Vector 儲存在堆積 (Heap) 中。堆疊通常比堆積更快,因此在不需要動態調整大小的情況下,std::array
是更好的選擇。
範例:Tetris 程式碼審查
在 Tetris 程式碼審查中,get_cell_colors
函數返回一個顏色向量。由於顏色數量固定(8 種),因此使用 std::array<Color, 8>
會更有效率。
如何正確使用 STD Vector
避免不必要的複製
除非必要,否則避免複製 Vector。將 Vector 作為函數參數傳遞時,應使用 const 引用 (const &
),以避免不必要的記憶體分配和元素複製。
預先分配記憶體
瞭解 Vector 的記憶體管理
Vector 在容量不足時會自動重新分配記憶體。在 MSVC 編譯器中,Vector 會以 50% 的幅度增長容量(例如,從 2 增加到 3)。Clang 也有類似的行為,而 GCC 則會將容量翻倍。這種自動增長可能導致頻繁的記憶體重新分配,降低效能。
使用 reserve()
預留空間
若預先知道 Vector 大約需要儲存多少元素,可以使用 reserve()
函數預留足夠的記憶體空間,避免不必要的重新分配。
使用 resize()
設定大小
resize()
函數可以設定 Vector 的大小,並使用預設建構子初始化新增的元素。如果之後需要覆寫這些元素,則效率不如 reserve()
。
避免複製,善用 emplace_back()
避免不必要的物件複製
盡可能避免在 Vector 中儲存物件時進行複製。複製物件可能會導致額外的記憶體分配和效能損失。
使用 emplace_back()
取代 push_back()
使用 emplace_back()
函數可以直接在 Vector 的記憶體空間中建構物件,避免先建構物件再複製到 Vector 的過程。這可以顯著提升效能。
移動語意 (Move Semantics)
了解 Move Semantics 的優勢
在 C++ 中,Move Semantics 允許將資源(例如堆積記憶體)從一個物件轉移到另一個物件,而無需進行複製。這可以大幅提升效能,尤其是在處理包含大量資料的物件時。
使用 Move Constructor
若類別包含指標或需要管理資源,應實作 Move Constructor。Move Constructor 可以將資源從來源物件轉移到新物件,並將來源物件設定為有效但未定義的狀態。
emplace_back()
與 Move Semantics 的結合
即使使用了 Move Constructor,emplace_back()
仍然比 push_back()
更有效率,因為它避免了物件的建構和轉移過程。
總結
- 評估是否需要 STD Vector:若不需要動態大小,使用
std::array
。 - 預先分配記憶體:使用
reserve()
或resize()
避免頻繁的重新分配。 - 避免複製:使用
emplace_back()
直接在 Vector 中建構物件。
透過遵循這些原則,可以更有效地使用 STD Vector,提升程式碼效能。