c++ 容器類 介紹

C++容器類

C++中的容器類包括“順序存儲結構”和“關聯存儲結構”,前者包括vector,list,deque等;後者包括set,map,multiset,multimap等。

若需要存儲的元素數在編譯器間就可以確定,可以使用數組來存儲,否則,就需要用到容器類了。

1、vector
連續存儲結構,每個元素是在內存上是連續的;
支持高效的隨機訪問和在尾端插入/刪除操作,但其他位置的插入/刪除操作效率低下;
2、deque
連續存儲結構,即其每個元素在內存上也是連續的,類似於vector,不同之處在於,deque提供了兩級數組結構,第一級完全類似於vector,代表實際容器;另一級維護容器的首位地址。
這樣,deque除了具有vector的所有功能外,還支持高效的首端插入/刪除操作。
3、list
非連續存儲結構,具有雙鏈表結構,每個元素維護一對前向和後向指針,因此支持前向/後向遍歷。
支持高效的隨機插入/刪除操作,但隨機訪問效率低下,且由於需要額外維護指針,開銷也比較大。

4、vector V.S. list V.S. deque:
a、若需要隨機訪問操作,則選擇vector;
b、若已經知道需要存儲元素的數目, 則選擇vector;
c、若需要隨機插入/刪除(不僅僅在兩端),則選擇list
d、只有需要在首端進行插入/刪除操作的時候,才選擇deque,否則都選擇vector。
e、若既需要隨機插入/刪除,又需要隨機訪問,則需要在vector與list間做個折中。
f、當要存儲的是大型負責類對象時,list要優於vector;當然這時候也可以用vector來存儲指向對象的指針,同樣會取得較高的效率,但是指針的維護非常容易出錯,因此不推薦使用。

5、capacity V.S size
a、capacity是容器需要增長之前,能夠盛的元素總數;只有連續存儲的容器纔有capacity的概念(例如vector,deque,string),list不需要capacity。
b、size是容器當前存儲的元素的數目。
c、vector默認的容量初始值,以及增長規則是依賴於編譯器的。

6、用vector存儲自定義類對象時,自定義類對象須滿足:
a、有可供調用的無參構造函數(默認的或自定義的);
b、有可用的拷貝賦值函數(默認的或自定義的)

7、迭代器iterator
a、vector與deque的迭代器支持算術運算,list的迭代器只能進行++/--操作,不支持普通的算術運算。

以下爲整個列表概述:

標準容器類
 說明
 
順序性容器
 
vector
 從後面快速的插入與刪除,直接訪問任何元素
 
deque
 從前面或後面快速的插入與刪除,直接訪問任何元素
 
list
 雙鏈表,從任何地方快速插入與刪除
 
關聯容器
 
set
 快速查找,不允許重複值
 
multiset
 快速查找,允許重複值
 
map
 一對多映射,基於關鍵字快速查找,不允許重複值
 
multimap
 一對多映射,基於關鍵字快速查找,允許重複值
 
容器適配器
 
stack
 後進先出
 
queue
 先進先出
 
priority_queue
 最高優先級元素總是第一個出列
 

所有標準庫共有函數

默認構造函數
 提供容器默認初始化的構造函數。
 
複製構造函數
 將容器初始化爲現有同類容器副本的構造函數
 
析構函數
 不再需要容器時進行內存整理的析構函數
 
empty
 容器中沒有元素時返回true,否則返回false
 
max_size
 返回容器中最大元素個數
 
size
 返回容器中當前元素個數
 
operator=
 將一個容器賦給另一個容器
 
operator<
 如果第一個容器小於第二個容器,返回true,否則返回false,
 
operator<=
 如果第一個容器小於或等於第二個容器,返回true,否則返回false
 
operator>
 如果第一個容器大於第二個容器,返回true,否則返回false
 
operator>=
 如果第一個容器大於或等於第二個容器,返回true,否則返回false
 
operator==
 如果第一個容器等於第二個容器,返回true,否則返回false
 
operator!=
 如果第一個容器不等於第二個容器,返回true,否則返回false
 
swap
 交換兩個容器的元素
 

其中operator>,operator>=,operator<,operator<=,operator==,operator!=均不適用於priority_queue

順序容器和關聯容器共有函數

begin
 該函數兩個版本返回iterator或const_iterator,引用容器第一個元素
 
end
 該函數兩個版本返回iterator或const_iterator,引用容器最後一個元素後面一位
 
rbegin
 該函數兩個版本返回reverse_iterator或const_reverse_iterator,引用容器最後一個元素
 
rend
 該函數兩個版本返回reverse_iterator或const_reverse_iterator,引用容器第一個元素前面一位
 
erase
 從容器中清除一個或幾個元素
 
clear
 清除容器中所有元素
 

下表顯示了順序容器和關聯容器中常用的typedef,這些typedef常用於變量、參數和函數返回值的一般性聲明。

value_type
 容器中存放元素的類型
 
reference
 容器中存放元素類型的引用
 
const_reference
 容器中存放元素類型的常量引用,這種引用只能讀取容器中的元素和進行const操作
 
pointer
 容器中存放元素類型的指針
 
iterator
 指向容器中存放元素類型的迭代器
 
const_iterator
 指向容器中存放元素類型的常量迭代器,只能讀取容器中的元素
 
reverse_iterator
 指向容器中存放元素類型的逆向迭代器,這種迭代器在容器中逆向迭代
 
const_reverse_iterator
 指向容器中存放元素類型的逆向迭代器,只能讀取容器中的元素
 
difference_type
 引用相同容器的兩個迭代器相減結果的類型(list和關聯容器沒有定義operator-)
 
size_type
 用於計算容器中項目數和檢索順序容器的類型(不能對list檢索)
 

三、序列類容器

vector


向量 相當於一個數組
在內存中分配一塊連續的內存空間進行存儲。支持不指定vector大小的存儲。STL內部實現時,首先分配一個非常大的內存空間預備進行存儲,即capacituy()函數返回的大小,當超過此分配的空間時再整體重新放分配一塊內存存儲,這給人以vector可以不指定vector即一個連續內存的大小的感覺。通常此默認的內存分配能完成大部分情況下的存儲。
優點:(1) 不指定一塊內存大小的數組的連續存儲,即可以像數組一樣操作,但可以對此數組
進行動態操作。通常體現在push_back() pop_back()
(2) 隨機訪問方便,即支持[ ]操作符和vector.at()
(3) 節省空間。
缺點:(1) 在內部進行插入刪除操作效率低。
(2) 只能在vector的最後進行push和pop,不能在vector的頭進行push和pop。
(3) 當動態添加的數據超過vector默認分配的大小時要進行整體的重新分配、拷貝與釋

2 list
雙向鏈表
每一個結點都包括一個信息快Info、一個前驅指針Pre、一個後驅指針Post。可以不分配必須的內存大小方便的進行添加和刪除操作。使用的是非連續的內存空間進行存儲。
優點:(1) 不使用連續內存完成動態操作。
(2) 在內部方便的進行插入和刪除操作
(3) 可在兩端進行push、pop
缺點:(1) 不能進行內部的隨機訪問,即不支持[ ]操作符和vector.at()
(2) 相對於verctor佔用內存多

3 deque
雙端隊列 double-end queue
deque是在功能上合併了vector和list。
優點:(1) 隨機訪問方便,即支持[ ]操作符和vector.at()
(2) 在內部方便的進行插入和刪除操作
(3) 可在兩端進行push、pop
缺點:(1) 佔用內存多

使用區別:
1 如果你需要高效的隨即存取,而不在乎插入和刪除的效率,使用vector
2 如果你需要大量的插入和刪除,而不關心隨即存取,則應使用list
3 如果你需要隨即存取,而且關心兩端數據的插入和刪除,則應使用deque

 

Qt容器類


Sequentail Containers
QVector<T> array-like data structure(在尾部插入數據時效率很高,而在中間和頭部插入數據時開銷很大)。
QVector提供了[]運算符
QVector可以用<<運算符代替append()函數。
QVector中的基本類型及指針被初始化爲0。
QLinkedList<T>
QLinkedList不提供[]運算符,所以必須通過迭代器來對其進行遍歷。
QList<T> array-list:綜合了QVector<T>和QLinkedList<T>最重要的優點: 支持[]運算符
在頭部或尾部的插入/刪除操作很迅速,而尺寸在1000以下時,在中間的插入/刪除操作也很迅速。
通常情況下,QList是最合適的通用型容器。
QStringList: QList<QString>的子類,在Qt中的API中被廣泛使用
QStack<T> 和QQueue<T>是Qt提供的兩個convenience subclasses,QStack<T>實際上是一個額外提供了push(),pop()top()接口的QVector,而 QQueue<T>實際是一個額外提供了enqueue()和dequeue()和head()的QList。
容器中可以放置的類必須擁有default constructor、copy constructor 和 assignment operator(顯式定義或由編譯器生成)
注意,派生自QObject的類不符合上述要求,因爲其不具備copy constructor和assignment operator;解決方法是在容器中存儲對象指針而不是對象本身。
容器中所存放的元素本身也可以是容器,即可以嵌套——不過需要注意將連續的尖括號用空格分隔開,以免編譯器誤認爲>>運算符。
Iterator
Qt支持兩種風格的迭代器——Java-style和STL-style
Java-style的迭代器更容易使用,而STL-style的迭代器可以同Qt和STL中的算法聯合使用,更爲強大。
Java-style Iterator
每個sequential容器類,都有兩個Java-style的迭代器類型:只讀迭代器和讀寫迭代器。
在使用Java-style的迭代器時,要清楚的第一件事情就是:迭代器並不直接指向容器中的元素,而是指向元素之前或之後的位置。迭代器被初始化時指向容器中第一個元素之前;若迭代器的右側有元素存在,hasNext()函數返回true;next()函數返回位於迭代器右側的元素,並將迭代器向右方移動一個元素的位置;hasPrevious()和previous()函數執行反方向的操作。
remove()函數總是刪除最近一次被跳過的那個元素。
setValue()函數總是對最近一次被跳過的那個元素執行更新操作
insert()函數在迭代器當前指向的位置處插入新元素,並將迭代器指向新元素及其後續元素之間的位置。


STL-style Iterator
每個sequential容器類,都有兩個STL-style的迭代器類型:Container<T>::iterator和Container<T>::const_iterator。
容器的begin()函數返回一個指向容器中頭部元素的iterator,而end()返回指向容器中尾部元素之後位置的iterator;
在容器爲空時,begin()和end()的結果相同。
通常通過調用isEmpty()來檢查容器是否爲空,而不是通過比較begin()和end()的結果。
可以對STL-style的iterator使用+、-、*這三個運算符,類似於指針的用法。
某些Qt函數的返回值是容器類;如果需要使用STL-style的迭代器來對這樣的返回值進行遍歷,必須保存返回值的一個副本,並在副本上完成遍歷,否則會可能會導致所謂的"dangling iterator"。
注意,若使用java-style的只讀迭代器,在這種情況下會隱式的完成複製的工作,保證迭代器總是在副本上進行遍歷操作。
implicit sharing(copy on write)
Qt中的implicit sharing機制的美妙之處在於它鼓勵程序員在返回對象時採用傳值這種簡明的方式而不是引用或指針。
STL與此相反,鼓勵程序員使用non-const引用來傳遞vector以避免將函數返回值的複製開銷。
Qt中所有的容器都採用了implicit sharing機制;此外很多其他類QByteArray,QBrush,QFont,QImage,QString也採用了該機制——這保證這些類在以傳值方式進行傳遞時有很高的效率,無論是作爲參數還是函數返回值。
在Qt提供的implicit sharing機制下,對vector或list執行只讀操作時,採用at()而不是[]運算符是一個更好的選擇。
類似的,儘可能的使用constBegin()和constEnd()以避免不必要的拷貝操作。
foreach syntax
foreach在進入循環體時自動複製容器的副本並在此副本上進行迭代,因此如果迭代過程中有通過迭代器對容器的修改操作的話,並不會影響循環的進行,循環結束後容器的內容也不會發生變化。
當然,如果在foreach循環中直接使用[]運算符對容器進行寫操作的話,容器內容自然會發生變化。
foreach中支持break和continue語句
Associative Containers


QMap
QMap中的key-value對是升序排列的
插入和刪除操作中都可以使用[]運算符,其下標爲key;爲避免創建不必要的空值,推薦用vlaue()而不是[]從QMap中取值。
QMap<K,T>中的K和T除了要求具備默認構造函數、拷貝構造函數和賦值運算符外,K還必須支持operator <,因爲這樣才能實現前面提到的升序排列。
keys() & values()
QMap的特性是單值;QMultiMap<K,T>則支持同一關鍵字下多值的存在,插入操作由insertMulti()完成


QHash
QHash提供的接口和QMap很相似
QHash<K,T>中的K要符合的額外要求:支持operator ==,並且K可用全局函數qHash()來計算hash value
QHash通常是單值的,而QMultiHash則通過insertMulti()支持多值插入。
QCache<K,T> & QSet<K>
遍歷associative containr的最簡單方法是使用Java-style的迭代器
foreach syntax也可用於assocaitive container


Generic Algorithms
頭文件<QAlgorithms>中聲明瞭一組全局模板函數,用於實現作用於容器的基本算法;多數算法都通過STL-style的迭代器來完成。
STL頭文件<algorithm>中的函數,即可作用於Qt容器,也可作用於STL容器。
qFind(),qBinaryFind(),qFill(),qCopy(),qSort(),qStableSorg(),qDeleteAll(),qSwap()
需要注意的是,qDeleteAll()只對包含指針的容器有意義,該函數將釋放所有對象,但並不刪除容器中的指針。
Strings,Byte Arrays,and Variants
QString,QByteArray和QVariant這三個類和容器類有很多相似之處,在某些場合下可作爲容器類的替代品;和容器類一樣,這三個類也應用了implicit sharing 機制

 

QString
QString中存放的是16-bit的Unicold值。
從概念上,QString可以看成是QVector<QChar>。
QString提供+/+=運算符以及append()函數用於合併字符串
QString提供的sprintf()函數,與C++標準庫中的sprintf支持相同的參數格式。
QString提供的arg()函數,是比sprintf()更好的選擇,因爲它保證類型安全,支持unicode。
QString::number(): number->string
QString::setNum(): number->string
反向的轉換接口:toInt(),toLongLong(),toDouble()等,這些函數都有一個可選參數——bool類型的指針,若轉換失敗則將該bool變量置爲true,否則置false.
mid()函數返回指定區間內的子串;left()和right()函數則分別返回左子串和右子串
QString的indexOf()函數可用於文本匹配,返回所匹配子串的起始下標;匹配失敗時返回值爲-1。
startsWith()和endsWith()函數可用於判斷字符串的首部和尾部是否符合某種模式。
QString在進行比較時是大小寫敏感的;當所比較的字符串是用戶可見時,使用localeAwareCompare()通常是正確的選擇。
toLower() & toUpper()
replace() & remove() & insert()
trimmed() & simplified()——這兩個函數用於消去字符串中的whitesapce(spaces,tabs,newlines等)
QString::split()將一個字符串分割爲子串,並返回由這些子串們組成的一個QStringList。
QStringList中的所有元素可以通過join()函數組成一個新的字符串,join()的參數在合併時會被插入相鄰元素中間。
在大多數情況下,由const char * 至QString的轉換是自動的。
反方向的轉換,可通過toAscii()或toLatin1()來完成;這些函數返回一個QByteArray,其可以通過QByteArray::data或QByteArray::constData()來轉換爲const char * ;
爲了簡化轉換,Qt提供了一個qPrintable()宏用於完成與toAscii()以及constData()相同的操作。

 

QByteArray
QByteArray提供的API與QString的很相似。
QByteArray的用處在於存儲原始2進制數據及8-bit編碼的字符串。

通常選擇QString而不是QByteArray來存儲文本信息,因爲QString支持Unicode。
QByteArray會自動在最後一個元素之後補上‘\0',這樣使得將QByteArray傳遞給需要const char *的函數變得很容易。
QByteArray支持'\0',允許存儲任意的2進制數據。

 

QVariant
QVariant類可用於存放很多Qt類型的值,並且還可以存放容器。
QVariant在 item view class,database model 和QSetting中被廣泛的使用着。
利用QVariant和嵌套,可以創建非常複雜的數據結構。
QVariant的便利性是以性能和代碼的可讀性爲代價的。
QVariant被Qt的meta-object system所使用,因此是Qtcore module的組成部分。
QVariant也可以支持用戶自定義的數據類型,前提是該類型具有defalut constructor和copy constructor。要實現對用戶自定義類型的支持,需要使用宏Q_DECLARE來註冊該類型。
全局函數:qVariantFromValue(),qVariantValue<T>(),qVariantCanConvert<T>()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章