2014-01-01 22:20:39
6、STL容器
STL容器都必須滿足那些條件。三個最核心的能力是:
-
所有容器提供的都是「value語意」而非「reference語意」。容器進行元素的安插動作時,內部實施的是拷貝動作,置於容器內。因此STL容器的每一個元素都必須能夠被拷貝。如果你意圖存放的物件不具有public copy建構式,或者你要的不是副本(例如你要的是被多個容器共同容納的元素),那麽容器元素就只能是指標(指向物件)。5.10.2節, p135 對此有所描述。
-
總體而言,所有元素形成一個次序(order)。也就是說,你可以依相同次序一次或多次巡訪每個元素。每個容器都提供「可傳回迭代器」的函式,運用那些迭代器你就可以巡訪元素。這是STL演算法賴以生存的關鍵介面。
-
一般而言,各項操作並非絕對安全。呼叫者必須確保傳給操作函式的引數符合需求。違反這些需求(例如使用非法索引)會導致未定義的行爲。通常STL自己不會丟擲異常。如果STL 容器所呼叫的使用者自定操作(user-definedoperations)擲出異常,會導致各不相同的行為。參見5.11.2節, p139。
6.2 Vectors
vector模塑出一個dynamic array。因此,它本身是「將元素置於dynamic array中加以管理」的一個抽象概念(圖6.1)。不過請注意,C++ Standard並未要求必須以dynamic array實作vector,只是規定了相應條件和操作複雜度。
其中,型別vector是一個定義於namespace std內的template:
namespace std {
template <class T,
class Allocator = allocator<T> >
class vector;
}
vector的元素可以是任意型別T,但必須具備assignable和copyable兩個性質。第二個template參數可有可無,用來定義記憶體模型(memory model,參 見15章)。預設的記憶體模型是C++ 標準程式庫提供的allocator。
6.2.1 Vectors的能力
vectors 之中用於操作大小的函式有size(),empty(),max_size()(6.1.2 節,p144)。另一個與大小有關的函式是capacity(),傳回vector實際能夠容納的元素數量。如果超越這個數量,vector就有必要重新配置內部記憶體。
vector的容量之所以很重要,有以下兩個原因:
-
一旦記憶體重新配置,和vector元素相關的所有references、pointers、iterators都會失效。
-
記憶體重新配置很耗時間。
所以如果你的程式管理了和vector元素相關的references、pointers、iterators,或如果執行速度對你而言至關重要,那麽就必須考慮容量問題。
你可以使用reserve()保留適當容量,避免一再重新配置記憶體。如此一來,只要保留的容量尚有餘裕,就不必擔心references失效。
std::vector<int> v; // create an empty
vectorv.reserve(80); // reserve memory for 80 elements
事實上爲了防止記憶體破碎,在許多實作方案中即使你不呼叫reserve(),當你第一次安插元素時也會一口氣分配整塊記憶體(例如2K)。如果你有一大堆vectors,每個vector的實際元素卻寥寥無幾,那麽浪費的記憶體相當可觀。
既然vectors的容量不會縮減,我們便可確定,即使刪除元素,其references、pointers、iterators 也會繼續有效,繼續指向動作發生前的位置。然而安插動作卻可能使references、pointers、iterators失效(譯註:因為安插可能導致vector重新配置)。
這裏有一個間接縮減vector容量的小竅門。注意,兩個vectors交換內容後,兩者的容量也會互換,因此下面的例子雖然保留了元素,卻縮減了容量:
template <class T>
void shrinkCapacity(std::vector<T>& v)
{
std::vector<T> tmp(v); // copy elements into a new vector
v.swap(tmp); // swap internal vector data
}
你甚至可以利用下面的述句直接縮減容量:
// shrink capacity of vector v for type T
std::vector<T>(v).swap(v);
不過請注意,swap()之後原先所有的references、pointers、iterators 都換了指涉對象;它們仍然指向原本位置。換句話說上述的shrinkCapacity()使所有references、pointers、iterators失效。