Qt容器类(总结)(新发现的QQueue和QStack,注意全都是泛型)
Introduction
Qt庫提供了一組基于模板的一般化的容器類。這些容器可以存儲指定的類型的元素。例如,如果你需要一個(gè)可變大小的Qstring數(shù)組,可以用QVector<QString>.。
這些容器比STL容器更輕更安全更容易使用。如果你不熟悉STL或者更喜歡以Qt的方式做事,你可以用這些類取代STL類。
這些類是隱式共享的,它們都是可重入,它們進(jìn)行了速度優(yōu)化,用更少的內(nèi)存和最小的內(nèi)聯(lián)代碼擴(kuò)展,生成更小的可執(zhí)行文件。此外,當(dāng)所有的線程僅僅以只讀的方式訪問它們時(shí),它們是線程安全的。
為了遍歷容器的元素,你可以用?Java-style iterators?或者?STL-style iterators.?。?Java-style iterators更容易使用和提供更高級的函數(shù)。STL-style iterators?效率稍微高一些,而且可以喝Qt的或STL的通用算法一起使用。
Qt也提供關(guān)鍵字foreach?使得很容易遍歷容易內(nèi)的元素。
The Container Classes
Qt提供了以下順序容器:QList,?QLinkedList,?QVector,?QStack,和?QQueue.?。對于大多數(shù)程序而言,QList?是最好用的,即使它以數(shù)組列表實(shí)現(xiàn),它提供了非??斓脑谇懊婧秃竺孀芳拥暮瘮?shù)。如果你真的需要一個(gè)連接表,可以用QLinkedList;。如果你想讓元素占用連續(xù)的內(nèi)存空間,可以用?QVector.?QStack?and?QQueue,它們提供了后進(jìn)先出和先進(jìn)先出。
Qt也提供了關(guān)聯(lián)式容器:?QMap,?QMultiMap,?QHash,?QMultiHash,and?QSet.?。Multi容器支持多個(gè)value關(guān)聯(lián)到單一的key。Hash容器提供了通過hash函數(shù)快速查找取代二分查找。
特殊情況下,?QCache?和?QContiguousCache?提供了在有限的cache?中高效的查找對象。
| Class | Summary |
| QList<T> | This is by far the most commonly used container class. It stores a list of values of a given type (T) that can be accessed by index. Internally, the?QList?is implemented using an array, ensuring that index-based access is very fast. Items can be added at either end of the list using?QList::append() and?QList::prepend(), or they can be inserted in the middle using?QList::insert(). More than any other container class,QList?is highly optimized to expand to as little code as possible in the executable.QStringList?inherits from?QList<QString>. |
| QLinkedList<T> | This is similar to?QList, except that it uses iterators rather than integer indexes to access items. It also provides better performance than?QList?when inserting in the middle of a huge list, and it has nicer iterator semantics. (Iterators pointing to an item in a?QLinkedList?remain valid as long as the item exists, whereas iterators to a?QList?can become invalid after any insertion or removal.) |
| QVector<T> | This stores an array of values of a given type at adjacent positions in memory. Inserting at the front or in the middle of a vector can be quite slow, because it can lead to large numbers of items having to be moved by one position in memory. |
| QStack<T> | This is a convenience subclass of?QVector?that provides "last in, first out" (LIFO) semantics. It adds the following functions to those already present in?QVector:?push(),?pop(), and?top(). |
| QQueue<T> | This is a convenience subclass of?QList?that provides "first in, first out" (FIFO) semantics. It adds the following functions to those already present in?QList:?enqueue(),?dequeue(), and?head(). |
| QSet<T> | This provides a single-valued mathematical set with fast lookups. |
| QMap<Key, T> | This provides a dictionary (associative array) that maps keys of type Key to values of type T. Normally each key is associated with a single value.?QMap?stores its data in Key order; if order doesn't matter?QHash?is a faster alternative. |
| QMultiMap<Key, T> | This is a convenience subclass of?QMap?that provides a nice interface for multi-valued maps, i.e. maps where one key can be associated with multiple values. |
| QHash<Key, T> | This has almost the same API as?QMap, but provides significantly faster lookups.?QHash?stores its data in an arbitrary order. |
| QMultiHash<Key, T> | This is a convenience subclass of?QHash?that provides a nice interface for multi-valued hashes. |
容器可以嵌套。例如,當(dāng)key的類型是Qstring和value類型是Qlist<int>,完全可以用?QMap<QString,?QList<int>>。缺陷就是必須在> >之間插入空格。否則C++編譯器會錯(cuò)把> >,翻譯成右移位操作并報(bào)告語法錯(cuò)誤。
這些容器被定義在不同的頭文件。為了方便,這些容器在文件<QtContainerFwd>.進(jìn)行了前置聲明。
容器保存的類型可以是任意可賦值的數(shù)據(jù)類型。因此要用容器存儲,一個(gè)類型必須提供默認(rèn)構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值操作。這包含了大部分可能用容器存儲的數(shù)據(jù)類型,包括基本類型如int和double,指針和Qt數(shù)據(jù)類型如Qstring,Qdate和Qtime。但是不包含QObject以及任意QObject的子類。如果你視圖實(shí)例化QList<QWidget>,編譯器將抱怨QWidget的拷貝構(gòu)造函數(shù)和賦值操作是不可用的。如果你想用容器存儲這些對象,可以以指針的方式進(jìn)行保存,如QList<QWidget?*>.。
以下是滿足可賦值的自定義數(shù)據(jù)類型的例子:
class?Employee
{
public:
??? Employee() {}
??? Employee(const?Employee?&other);
?
??? Employee?&operator=(const?Employee?&other);
?
private:
????QString?myName;
????QDate?myDateOfBirth;
};
如果我們不提供拷貝構(gòu)造函數(shù)或賦值操作,C++將提供默認(rèn)的實(shí)現(xiàn)成員拷貝。在以上的例子,這么寫就足夠了。如果你沒有提供任何構(gòu)造函數(shù),C++將提供默認(rèn)構(gòu)造函數(shù),用成員的默認(rèn)構(gòu)造函數(shù)進(jìn)行成員初始化。即使沒有提供任何顯示的構(gòu)造函數(shù)或賦值操作,下面的數(shù)據(jù)類型也可以用容器存儲。
struct Movie
{
????int?id;
????QStringtitle;
????QDatereleaseDate;
};
一些容器對于要進(jìn)行存儲的數(shù)據(jù)類型有額外的要求,例如,QMap<Key,T>的key類型必須提供operator<()。這樣的特殊要求在類的詳細(xì)描述中都有介紹。在一些情況下,特殊的函數(shù)有特殊的要求,這些只是每個(gè)函數(shù)的基礎(chǔ)描述,當(dāng)要求不滿足時(shí)編譯器將報(bào)錯(cuò)。
Qt容器提供operator<<()?和operator>>() 所以可以很容易用?QDataStream進(jìn)行讀寫。這意味著存儲在容器中的數(shù)據(jù)類型也必須支持operator<<() 和 operator>>()。這樣的支持很簡單:
QDataStream&operator<<(QDataStream&out, const Movie &movie)
{
??? out << (quint32)movie.id<< movie.title
??????? << movie.releaseDate;
??? return out;
}
?
QDataStream&operator>>(QDataStream&in, Movie &movie)
{
????quint32id;
????QDatedate;
?
??? in >> id >> movie.title>> date;
??? movie.id = (int)id;
??? movie.releaseDate = date;
??? return in;
}
某些容器類的函數(shù)的文檔說明設(shè)計(jì)默認(rèn)構(gòu)造值,例如,?QVector用默認(rèn)構(gòu)造值自動(dòng)的初始化其元素。如果沒有指定key值,則QMap::value()?返回默認(rèn)構(gòu)造值。對于大多數(shù)值類型,這意味著簡單的用默認(rèn)構(gòu)造函數(shù)創(chuàng)建默認(rèn)值。但是對于原始類型如int,double和指針,C++語言不指定任何初始化,在這種情況下,Qt容器自動(dòng)的初始化為0.
The Iterator Classes
迭代器提供了訪問容器元素的統(tǒng)一方法。Qt容器提供兩種迭代器:Java-style iterators and STL-style iterators.。當(dāng)容器中的數(shù)據(jù)被修改或者由于調(diào)用非const成員函數(shù)生成隱式共享數(shù)據(jù)副本時(shí),這兩種迭代器都會無效。
Java-StyleIterators
Java-style iterators是在Qt4才新加入的而且Qt程序中標(biāo)準(zhǔn)的使用著。它們比STL-styleiterators用起來方便,但是效率稍微低了一點(diǎn)。它們的API仿照java的迭代器類。
對于美國容器類,有兩種Java-style iterator數(shù)據(jù)類型,一種是只讀,一種可讀寫:
ContainersRead-only iteratorRead-write
| Containers | Read-only iterator | Read-write iterator |
| QList<T>,?QQueue<T> | QListIterator<T> | QMutableListIterator<T> |
| QLinkedList<T> | QLinkedListIterator<T> | QMutableLinkedListIterator<T> |
| QVector<T>,?QStack<T> | QVectorIterator<T> | QMutableVectorIterator<T> |
| QSet<T> | QSetIterator<T> | QMutableSetIterator<T> |
| QMap<Key, T>,?QMultiMap<Key, T> | QMapIterator<Key, T> | QMutableMapIterator<Key, T> |
| QHash<Key, T>,?QMultiHash<Key, T> | QHashIterator<Key, T> | QMutableHashIterator<Key, T> |
在此討論中,我們只關(guān)注?QList?和?QMap。?QLinkedList,?QVector,和?QSet的迭代器跟Qlist的完全一樣,同用的,?QHash?的迭代器也跟?QMap的完全一樣。
與STL-style iterators 不同的是, Java-style iterators指向元素的中間而不是直接指向元素的。因此,它們指向容器的開頭,容器的末尾或者兩個(gè)元素的之間。如下圖所示:
?
以下是典型的遍歷?QList<QString>并輸出到控制臺的循環(huán):
QList<QString>list;
list << "A" << "B" << "C" <<"D";
?
QListIterator<QString>i(list);
while (i.hasNext())
????qDebug()<< i.next();
它的工作原理如下:QList?的迭代傳遞到?QListIterator?的構(gòu)造函數(shù)。此時(shí),迭代器指向list的第一個(gè)元素之前。然后調(diào)用?hasNext()?檢測迭代器后面是否有元素,如果有,我們調(diào)用next()?跳過該元素,next()?返回所跳過的元素。對于?QList<QString>,其元素的類型是?QString.。
下面是反向遍歷QList:
QListIterator<QString>i(list);
i.toBack();
while (i.hasPrevious())
????qDebug()<< i.previous();
這段代碼與正向遍歷的對稱,除了開始調(diào)用toBack()?把迭代器移到最后元素的后面。
下圖說明了調(diào)用?next()?和?previous()?的效果:
?
下面總結(jié)了?QListIterator?API:
| Function | Behavior |
| toFront() | Moves the iterator to the front of the list (before the first item) |
| toBack() | Moves the iterator to the back of the list (after the last item) |
| hasNext() | Returns true if the iterator isn't at the back of the list |
| next() | Returns the next item and advances the iterator by one position |
| peekNext() | Returns the next item without moving the iterator |
| hasPrevious() | Returns true if the iterator isn't at the front of the list |
| previous() | Returns the previous item and moves the iterator back by one position |
| peekPrevious() | Returns the previous item without moving the iterator |
QListIterator不提供插入或刪除元素的函數(shù)。要實(shí)現(xiàn)插入刪除,可以用QMutableListIterator,下面例子從?QList<int>?中刪除奇書:
QMutableListIterator<int> i(list);
while (i.hasNext()) {
??? if (i.next() % 2 != 0)
??????? i.remove();
}
循環(huán)中每次調(diào)用next()都跳過后面的元素。remove()?函數(shù)從list中刪除最近跳過的元素。調(diào)用remove()?不會是迭代器失效,所以可以安全的跡象使用它。反向遍歷也一樣:
QMutableListIterator<int> i(list);
i.toBack();
while (i.hasPrevious()) {
??? if (i.previous() % 2 != 0)
??????? i.remove();
}
如果想改變存在的一個(gè)元素,可以用setValue().。
QMutableListIterator<int> i(list);
while (i.hasNext()) {
??? if (i.next() > 128)
??????? i.setValue(128);
}
和?remove()一樣,?setValue()作用于最近跳過的元素。如果是正向遍歷,這個(gè)元素在迭代器的前面,如果是反向遍歷,該元素在迭代器的后面。
?next()?返回一個(gè)元素的非const引用,對于簡單的操作,我們不需要setValue()::
QMutableListIterator<int> i(list);
while (i.hasNext())
??? i.next() *= 2;
如以上提到的,QLinkedList,,?QVector',, 和?QSet的迭代器的API 跟?QList的一樣?,F(xiàn)在來討論?QMapIterator,它遍歷(key,value)對,多少有些不同。
像?QListIterator一樣,,?QMapIterator?提供toFront(),?toBack(),?hasNext(),?next(),?peekNext(),?hasPrevious(),?previous(),和?peekPrevious().?。key和value通過next(),peekNext(), previous(), 或者 peekPrevious().的返回對象調(diào)用key()和value()來獲取。
QMap<QString,QString>map;
map.insert("Paris", "France");
map.insert("Guatemala City", "Guatemala");
map.insert("Mexico City", "Mexico");
map.insert("Moscow", "Russia");
...
?
QMutableMapIterator<QString,QString>i(map);
while (i.hasNext()) {
??? if (i.next().key().endsWith("City"))
??????? i.remove();
}
QMapIterator?也提供對于迭代器的key()和value()函數(shù),返回最近被跳過元素的key和value。
QMap<int,?QWidget*> map;
QHash<int,?QWidget*> hash;
?
QMapIterator<int,?QWidget*> i(map);
while (i.hasNext()) {
??? i.next();
??? hash.insert(i.key(), i.value());
}
如果想遍歷有相同value的元素,可以用?findNext()?或者?findPrevious().。
QMutableMapIterator<int,?QWidget*> i(map);
while (i.findNext(widget))
??? i.remove();
STL-StyleIterators
STL-style iterators?從Qt2.0開發(fā)使用。它們與Qt的和STL的通用算法兼容,并進(jìn)行了速度優(yōu)化。
對于每個(gè)容器,都有兩種STL-style iterator類型:一種是只讀,一種是可讀寫的。應(yīng)該進(jìn)可能的使用只讀的迭代器,因?yàn)樗瓤勺x寫的迭代器快。
| Containers | Read-only iterator | Read-write iterator |
| QList<T>,?QQueue<T> | QList<T>::const_iterator | QList<T>::iterator |
| QLinkedList<T> | QLinkedList<T>::const_iterator | QLinkedList<T>::iterator |
| QVector<T>,?QStack<T> | QVector<T>::const_iterator | QVector<T>::iterator |
| QSet<T> | QSet<T>::const_iterator | QSet<T>::iterator |
| QMap<Key, T>,?QMultiMap<Key, T> | QMap<Key, T>::const_iterator | QMap<Key, T>::iterator |
| QHash<Key, T>,?QMultiHash<Key, T> | QHash<Key, T>::const_iterator | QHash<Key, T>::iterator |
STL iterators?的API參照數(shù)組的指針。例如,++操作將迭代器指向下一個(gè)元素,* 操作返貨迭代器指向的元素。實(shí)際上,QVector?和?QStack,的元素存儲在相鄰的內(nèi)存位置,?iterator?僅僅是T *,的別名,?const_iterator?是?constT *.的別名。
在此討論中,我們只關(guān)注?QList?和?QMap.,?QLinkedList,?QVector,和?QSet?的迭代器與?QList的完全一樣,同樣的,?QHash?的迭代器和?QMap'的完全一樣。
QList<QString>list;
list << "A" << "B" << "C" <<"D";
?
QList<QString>::iteratori;
for (i = list.begin(); i != list.end(); ++i)
??? *i = (*i).toLower();
與?Java-styleiterators不同的是, STL-style iterators?直接指向元素,begin()返回指向容器的第一個(gè)元素的迭代器。end()?返回一個(gè)迭代器指向超過容器最后一個(gè)元素的虛擬的元素。end()?標(biāo)記無效的位置,不能解引用,典型的用于循環(huán)終止條件。如果list是空的,begin() 和 end(),相等,循環(huán)不會被執(zhí)行。
下圖的紅色箭頭顯示的是有效的迭代器位置:
?
用STL-style iterator?進(jìn)行反向迭代在訪問元素之前必須先自減。
QList<QString>list;
list << "A" << "B" << "C" <<"D";
?
QList<QString>::iteratori = list.end();
while (i != list.begin()) {
??? --i;
??? *i = (*i).toLower();
}
到目前為止的代碼中,我們用* 操作獲取元素的值,然后調(diào)用了QString::toLower()?。大部分的編譯器也支持?i->toLower(),,有的則不支持。
對于只讀,我們用const_iterator, constBegin(), 和 constEnd()
QList<QString>::const_iteratori;
for (i = list.constBegin(); i != list.constEnd(); ++i)
????qDebug()<< *i;
下表總結(jié)了STL-style iterators' API:
| Expression | Behavior |
| *i | Returns the current item |
| ++i | Advances the iterator to the next item |
| i += n | Advances the iterator by?n?items |
| --i | Moves the iterator back by one item |
| i -= n | Moves the iterator back by?n?items |
| i - j | Returns the number of items between iterators?i?and?j |
++和—操作都可以用做前綴和后綴。前綴修改迭代器并返回修改后的迭代器,后綴版本先復(fù)制迭代器,再修改,返回復(fù)制的副本。當(dāng)忽略返回值的表達(dá)式中,我們建議使用前綴版本,因?yàn)樗容^快一些。
對于非const迭代器類型,*操作的返回值可以用作復(fù)制操作的左值。
對于QMap?和?QHash,?*操作返回元素的value部分。如果你想獲得key,可以對迭代器調(diào)用key()。對應(yīng)的,迭代器也提供value()函數(shù)來獲得value。
QMap<int,?int> map;
...
QMap<int,?int>::const_iterator i;
for (i = map.constBegin(); i != map.constEnd(); ++i)
????qDebug()<< i.key() << ":" << i.value();
由于有隱式共享,函數(shù)返回容器代價(jià)不是很高。Qt的API包含了很多返回QList?或者?QStringList?的函數(shù)。如果你想用?STL?迭代器遍歷它們,應(yīng)該先拷貝容器并遍歷拷貝的容器副本:
// RIGHT
const?QList<int> sizes = splitter->sizes();
QList<int>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)
??? ...
?
// WRONG
QList<int>::const_iterator i;
for (i = splitter->sizes().begin();
??????? i != splitter->sizes().end();++i)
對于返回容器的const或者非const引用的函數(shù)并不會出現(xiàn)這種情況。
隱式共享在STL-style iterators的另外一個(gè)結(jié)果是:當(dāng)容器正在使用非const迭代器時(shí)不能拷貝容器,?Java-style iterators不受此限制。
The foreach Keyword
如果你只是想依次的遍歷容器的元素,可以用Qt的關(guān)鍵字foreach?,該關(guān)鍵字是Qt對C++額外添加的,用預(yù)處理器實(shí)現(xiàn)的。它的語法為:foreach?(variable,?container)?
QLinkedList<QString>list;
...
QStringstr;
foreach(str, list)
????qDebug()<< str;
foreach?代碼比使用迭代器的同等代碼要短:
QLinkedList<QString>list;
...
QLinkedListIterator<QString>i(list);
while (i.hasNext())
????qDebug()<< i.next();
如果數(shù)據(jù)類型不包含逗號,用于遍歷的變量可以聲明在foreach?之內(nèi):
QLinkedList<QString>list;
...
foreach(const?QString&str, list)
????qDebug()<< str;
和其他C++循環(huán)結(jié)構(gòu)一樣,你可以在foreach?循環(huán)內(nèi)用分支,可以可以用break跳出循環(huán):
QLinkedList<QString>list;
...
foreach(const?QString&str, list) {
??? if (str.isEmpty())
??????? break;
????qDebug()<< str;
}
對于QMap?和?QHash,,foreach?獲取的是?(key,value)?對的alue部分。如果你想遍歷key和alue,可以用迭代器或者下如下代碼:
QMap<QString,int> map;
...
foreach(const?QString&str, map.keys())
????qDebug()<< str << ":" << map.value(str);
For a multi-valued map:
QMultiMap<QString,int> map;
...
foreach(const?QString&str, map.uniqueKeys()) {
??? foreach (int?i, map.values(str))
????????qDebug()<< str << ":" << i;
}
當(dāng)進(jìn)入foreach?循環(huán)時(shí)Qt自動(dòng)拷貝容器。如果你在遍歷的時(shí)候改變了容器,也不會影響循環(huán)。(即使你不改變?nèi)萜?#xff0c;也一樣會進(jìn)行拷貝容器,多虧了隱式共享拷貝容器速度很快。)
由于foreach?創(chuàng)建了容器的副本,所以用非const變量并不能改變原始的容器,僅僅影響容器副本,這可能不是你想要的。
另外,Qt也提供了偽關(guān)鍵字forever?無限循環(huán):
forever {
??? ...
}
如果你擔(dān)心命名空間受污染,可以在?.pro文件中添加下面的代碼行禁止這些宏:
CONFIG += no_keywords
Other Container-LikeClasses
Qt包含了三個(gè)在某些方面類似容器的模板類,這些類不提供迭代器,而且不能用foreach?關(guān)鍵字:
·????????QVarLengthArray<T, Prealloc> provides a low-level variable-length array. It can beused instead of?QVector?in places where speed is particularly important.
·????????QCache<Key,T> provides a cache to store objects of a certain type T associated withkeys of type Key.
·????????QContiguousCache<T>provides an efficient way of caching data that is typically accessed in acontiguous way.
·????????QPair<T1,T2> stores a pair of elements.
另外的非模板類型為:?QBitArray,?QByteArray,?QString,和QStringList.。
Algorithmic Complexity
算法復(fù)雜度關(guān)心當(dāng)容器的元素增加時(shí)每個(gè)函數(shù)的運(yùn)行有多快。例如,在QLinkedList中間插入一個(gè)元素速度是很快的,不管里面存儲了多少個(gè)元素。但是,如果在QVector?中插入一個(gè)元素,如果QVector?包含有很多元素時(shí)代價(jià)可能會很大,因?yàn)橐话愕脑囟夹枰苿?dòng)一個(gè)位置。
為了描述算法復(fù)雜度,我們用以下基于big Oh的術(shù)語:
·????????Constant time:?O(1). A functionis said to run in constant time if it requires the same amount of time nomatter how many items are present in the container. One example is?QLinkedList::insert().
·????????Logarithmic time:?O(log?n).A function that runs in logarithmic time is a function whose running time isproportional to the logarithm of the number of items in the container. Oneexample is?qBinaryFind().
·????????Linear time:?O(n). A functionthat runs in linear time will execute in a time directly proportional to thenumber of items stored in the container. One example is?QVector::insert().
·????????Linear-logarithmic time:?O(n?log?n).A function that runs in linear-logarithmic time is asymptotically slower than alinear-time function, but faster than a quadratic-time function.
·????????Quadratic time:?O(n2). Aquadratic-time function executes in a time that is proportional to the squareof the number of items stored in the container.
下表總結(jié)了Qt順序容器類的算法復(fù)雜度:
| Index lookup | Insertion | Prepending | Appending | |
| QLinkedList<T> | O(n) | O(1) | O(1) | O(1) |
| QList<T> | O(1) | O(n) | Amort. O(1) | Amort. O(1) |
| QVector<T> | O(1) | O(n) | O(n) | Amort. O(1) |
在表中,"Amort." 代表"amortized behavior"。例如,"Amort.O(1)"意思是如果你只調(diào)用函數(shù)一次,復(fù)雜度可能是O(n)?,但是如果你多次調(diào)用函數(shù),平均復(fù)雜度則為O(1)。
下表總結(jié)了Qt關(guān)聯(lián)容器的算法復(fù)雜度:
| Key lookup | Insertion | |||
| Average | Worst case | Average | Worst case | |
| QMap<Key, T> | O(log?n) | O(log?n) | O(log?n) | O(log?n) |
| QMultiMap<Key, T> | O(log?n) | O(log?n) | O(log?n) | O(log?n) |
| QHash<Key, T> | Amort. O(1) | O(n) | Amort. O(1) | O(n) |
| QSet<Key> | Amort. O(1) | O(n) | Amort. O(1) | O(n) |
QVector,?QHash,和?QSet,?,在后面追加元素的復(fù)雜度為O(log?n).,通過調(diào)用QVector::reserve(),?QHash::reserve(),或者?QSet::reserve()?可以降到O(1)?。
Growth Strategies
QVector<T>,?QString,和?QByteArray?的元素存儲在連續(xù)的內(nèi)存中;QList<T>維護(hù)著一個(gè)指向其元素的指針數(shù)組以便快速索引元素(除非T是指針類型或者一個(gè)基本類型的指針的大小,此時(shí)值本身存在數(shù)組)。QHash<Key,T>?擁有一個(gè)hash表,hash表的大小與其中的元素的個(gè)數(shù)成比例,要避免每次在容器后面添加元素時(shí)都進(jìn)行內(nèi)存重新分配,這里類分配了多于實(shí)際需要的內(nèi)存。
考慮下面的代碼,我們從一個(gè)QString生成另一個(gè)QString:
QStringonlyLetters(const?QString&in)
{
????QStringout;
??? for (int?j = 0; j < in.size(); ++j) {
??????? if (in[j].isLetter())
??????????? out += in[j];
??? }
??? return out;
}
我們通過每次動(dòng)態(tài)的添加一個(gè)字符來創(chuàng)建一個(gè)字符串out。假設(shè)我們添加15000個(gè)字符到QString字符串。當(dāng)QString用完內(nèi)存之后進(jìn)行了以下18此內(nèi)存再分配:4, 8, 12, 16, 20, 52, 116, 244, 500, 1012, 2036, 4084, 6132, 8180,10228, 12276, 14324, 16372.。最后,QString字符串分配了16372個(gè)Unicode?字符的空間,其中15000個(gè)被占用。
上面的值看起來可能有點(diǎn)奇怪,下面是指導(dǎo)原則:
·????????QString?每次分配4個(gè)字符的空間知道其大小為20
·????????從20到4080其大小每次成倍增加,.更準(zhǔn)確的說,它增加到下一個(gè)2的冪次方減去?12.
·?????????從4080開始,它每次增加2048個(gè)字符。這很容易理解因?yàn)楝F(xiàn)代的操作系統(tǒng)當(dāng)再分配內(nèi)存是不拷貝整個(gè)數(shù)據(jù),物理內(nèi)存頁僅僅是被重新整理了,而且只有第一個(gè)和最后一個(gè)也需要拷貝。
QByteArray?和?QList<T>?用的算法有點(diǎn)像Qstring。
QVector<T>?對于可以用memcpy()?拷貝數(shù)據(jù)的也用那些算法,但是對于通過調(diào)用拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)拷貝的數(shù)據(jù)類型則用不同的算法。由于在那種情況下再分配內(nèi)存的代價(jià)很高,當(dāng)用完內(nèi)存的時(shí)候QVector<T>通過成倍增長以減少分配次數(shù)。
QHash<Key, T>則是完全不一樣的情況。Qhash的內(nèi)部hash表以平方次增長的,而且每次增長的元素被重定位到新的桶中,計(jì)算為?qHash(key) %?QHash::capacity()。這個(gè)備注也使用與?QSet<T> 和?QCache<Key, T>?。
對于大部分程序而言,Qt默認(rèn)提供的算法是起作用的。如果你需要更多的控制,QVector<T>,?QHash<Key, T>,?QSet<T>,?QString, and?QByteArray?提供了三個(gè)函數(shù)允許你檢查和指定需要用多少內(nèi)存來存儲元素:
·????????capacity()?返回分配的可以存儲元素的數(shù)量的內(nèi)存。
·????????reserve(size)?顯示的預(yù)先分配size個(gè)元素的內(nèi)存
·????????squeeze()?釋放不需要存儲元素的內(nèi)存。
·????????如果你知道容器大概要存儲多少個(gè)元素,你可以調(diào)用reserve(),,而且當(dāng)你完成填充容器,你可以調(diào)用squeeze()?釋放多余的預(yù)先分配的內(nèi)存。
總結(jié)
以上是生活随笔為你收集整理的Qt容器类(总结)(新发现的QQueue和QStack,注意全都是泛型)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。