容器是存儲數據的集合,序列容器則是指明它的存儲方式為序列式存儲。C++有三種序列式容器:vector,deque,list。其中,前兩種都是順序存儲方式,第三種則是指鏈表。為了實現算法和容器的分離,STL提供的這幾個容器都只實現了很少的操作,例如插入,刪除,以及對容器本身容量的設置等,而像排序,查找之類的操作則都是由算法庫去提供的。
從效率上而言,因為前兩者是順序存儲,所以他可支持隨機訪問,若按某種次序存放,在查找時則可有較高的效率,但在插入上,因為有後移操作,所以效率不高。list則正好相反。所以在容器的選擇上,我們應該根據需要的業務邏輯來選擇合適的容易。
提到容器,就不得不提迭代器,每一個容器都有自己的迭代器,利用它可以訪問容器內部的元素,它的使用方式和指針類似,實際上,指針可以看成是特殊的迭代器。以Vector為例,獲得迭代器的函數如下:
[cpp]
vector::begin(3)
Return iterator to beginning (public member function)
vector::end(3)
Return iterator to end (public member function)
vector::rbegin(3) Return reverse iterator to reverse beginning (public member function)
vector::rend(3) Return reverse iterator to reverse end (public member function)
[html] view plaincopy
<span style="white-space:pre"> </span>
在容器的其他各類方法中,迭代器也大量的被使用在其中,具體的還是參考文檔更好,這裡就不詳述了。
對於這三種容器,它們有很多共有的函數,像:push_back, pop_back, insert, erase等等,但同時某些容器也有它單獨的方法,例如deque和list還具有push_front, pop_front等函數方法,這個中的區別主要還在於這些容器的實現方式上。
string其實可以看做是一個特殊的序列式容器,它擁有幾乎所有的容器的基本函數,所以我們也能通過迭代去訪問string對象。
適配器
C++primer對適配器是這樣定義的:
A library type, function, or iterator that given a type, function, or iterator, makes it act like another. There are three sequential container adaptors: stack, queue , and priority_queue . Each of these adaptors defines a new interface on top of an underlying sequential container.
而我對它是這樣理解的,適配器就像一個接口,其底層可以封裝各類容器,它對外則表現出一種抽象類型,使得用戶只能通過抽象類型所提供的接口函數實現對底層容器的操作。
然後,在我的學習過程中,卻漸漸產生了一個疑惑,翻閱C++的幫助文檔,適配器一般只提供了這樣一種構造函數,例如stack:
[cpp]
explicit stack ( const Container& ctnr = Container() );
也就是說如果你不為其提供一個容器,它將使用自己默認的容器deque,而如果你為它提供了一個容器,它將維護的不是這個容器本身,而是它的一個拷貝。
A container adaptor keeps a container object as data. This container object is a copy of the argument passed to the constructor, if any, otherwise it is an empty container.
這樣看來,如果我已經持有了一個容器,那麼,我將無法使用適配器對該容器進行封裝操作,而只能封裝它的一個拷貝,通過接口執行的所有的操作只對副本有效,這樣似乎會導致這樣一種限制,那就是:只能使用一種適配器對容器進行封裝,並且封裝後,原來的容器應該被丟棄掉。存在這樣的限制將使在如下需求中應用適配器成為不可能,即:我持有一個容器,我希望它在不同的場合表現出不同的特征,例如某些場合中我希望它只表現出棧的特征,而在另一些場合中,則希望它表現出隊列的特征。限制它不能使用適配器的原因就在於適配器的拷貝機制,如果我分別使用棧和隊列去封裝它,那麼這兩個隊列將各自得到一個原始容器的拷貝,從而造成數據的不一致,這將使我通過不同的適配器對相同的容器進行操作成為不可能。
其實,在java中也有類似的概念,不同的是,它是可以使用接口去解決這個問題,例如一個容器可以實現棧的接口,也可以實現隊列的接口,我可以持有一個棧的引用變量和一個隊列的引用變量,並讓它們都指向這個容器,那麼我通過不同的引用去訪問這個容器就只能使用那個引用特有的方法,問題也就解決了。
STL在設計之初應該不可能沒有考慮到這個問題,是不是存在著這樣一個機制能夠解決這個問題呢?如果有哪位大神路過,請一定留下您的筆墨為小弟答疑解惑,小弟感激不盡。