最佳化 C++/程式碼最佳化/分配和釋放
即使使用非常高效的分配器,分配和釋放操作也需要相當的時間,而且分配器通常效率不高。
在本節中,將描述一些減少記憶體分配總數及其相應的釋放的技術。這些技術僅應用於瓶頸,即在測量到大量分配對效能有重大影響之後。
在瓶頸記憶體分配之前進行移動,並在瓶頸之後進行匹配的釋放。
可變長度動態記憶體管理比自動記憶體管理慢得多。
類似的最佳化也應針對間接導致分配的操作,例如直接或間接擁有動態記憶體的物件的副本。
在向向量或字串物件新增元素之前,呼叫其成員函式 reserve,並指定足夠大的大小以滿足大多數情況。
如果反覆向向量或字串物件新增物件,則會執行多次代價高昂的重新分配。為了避免此類重新分配,只需預先分配所需的記憶體即可。
要清空向量物件而不釋放其記憶體,請使用語句 x.resize(0); 要清空它並釋放其記憶體,請使用語句 vector<T>().swap(x);
要清空向量物件,還可以使用 clear() 成員函式,但 C++ 標準沒有指定此函式是否保留向量的分配容量。
雖然標準沒有指定容量是否會改變,但進一步的 測試 表明容量保持不變。此外,C++11 為此行為提供了 "shrink_to_fit()" 函式。
如果反覆填充和清空向量物件,因此要避免頻繁的重新分配,請透過呼叫 resize 成員函式來執行清空操作,根據標準,該函式保留物件的容量。相反,如果你已經完成對大型向量物件的訪問,並且可能不再使用它,或者將使用它來儲存數量明顯更少的元素,則應透過對新的空臨時向量物件呼叫 swap 函式來釋放物件的記憶體。
對於每個可複製的具體類 T,它直接或間接地擁有某些動態記憶體,重新定義相應的 swap 函式。
具體來說,向類新增具有以下簽名的公共成員函式
void swap(T&) throw();
並在包含類 T 的相同名稱空間中新增以下非成員函式
void swap(T& lhs, T& rhs) { lhs.swap(rhs); }
而且,如果該類不是類模板,請在包含類 T 定義的相同檔案中新增以下非成員函式
namespace std { template<> swap(T& lhs, T& rhs) { lhs.swap(rhs); } }
在標準庫中,swap 函式被許多演算法頻繁呼叫。該函式具有通用實現,以及針對標準庫中各種型別的專門實現。
如果在呼叫 swap 的標準庫演算法中使用了非標準類的物件,並且沒有過載 swap 函式,則將使用通用實現。
swap 函式的通用實現會導致建立和銷燬臨時物件,以及執行兩個物件賦值。如果應用於擁有動態記憶體的物件,此類操作會花費大量時間,因為此類記憶體將重新分配三次。
動態記憶體的擁有甚至可能是間接的。例如,如果成員變數是字串或向量,或者是一個包含字串或向量物件的類,那麼每次包含這些物件的類被複制時,這些物件擁有的記憶體都會被重新分配。因此,即使在這些情況下,也應該過載 swap 函式。
如果物件不擁有動態記憶體,則物件的複製速度快得多,而且不會明顯慢於使用其他技術,因此無需過載 swap 函式。
如果該類不可複製或抽象,則絕不應該對該型別的物件呼叫 swap 函式,因此在這些情況下也不應該重新定義 swap 函式。
要加快 swap 函式的速度,你必須為你的類專門化它。有兩種可能的實現方式:在類的相同名稱空間(可能是全域性名稱空間)中作為過載,或者在 std 名稱空間中作為標準模板的專門化。建議兩種方式都定義它,因為首先,如果它是一個類模板,則只有第一種方式是可行的,然後一些編譯器不接受或在只以第一種方式定義時發出警告。
此類函式的實現必須訪問物件的全部成員,因此需要呼叫一個成員函式,該函式按照慣例也被稱為 swap,它執行實際的工作。
此項工作包括交換兩個物件的全部非靜態成員,通常透過對它們呼叫 swap 函式來完成,無需限定其名稱空間。
要將 std::swap 函式放入當前作用域,該函式必須以以下語句開頭
using std::swap;