IntroductionQt庫提供了一組基于模板的一般化的容器類。這些容器可以存儲指定的類型的元素,。例如,,如果你需要一個可變大小的Qstring數(shù)組,,可以用QVector<QString>.。 這些容器比STL容器更輕更安全更容易使用,。如果你不熟悉STL或者更喜歡以Qt的方式做事,,你可以用這些類取代STL類,。 這些類是隱式共享的,,它們都是可重入,,它們進行了速度優(yōu)化,,用更少的內存和最小的內聯(lián)代碼擴展,,生成更小的可執(zhí)行文件,。此外,當所有的線程僅僅以只讀的方式訪問它們時,,它們是線程安全的。 為了遍歷容器的元素,,你可以用 Java-style iterators 或者 STL-style iterators. 。 Java-style iterators更容易使用和提供更高級的函數(shù),。STL-style iterators 效率稍微高一些,,而且可以喝Qt的或STL的通用算法一起使用。 Qt也提供關鍵字foreach 使得很容易遍歷容易內的元素,。 The Container ClassesQt提供了以下順序容器:QList, QLinkedList, QVector, QStack,和 QQueue. ,。對于大多數(shù)程序而言,QList 是最好用的,,即使它以數(shù)組列表實現(xiàn),,它提供了非常快的在前面和后面追加的函數(shù),。如果你真的需要一個連接表,可以用QLinkedList;,。如果你想讓元素占用連續(xù)的內存空間,,可以用 QVector. QStack and QQueue,它們提供了后進先出和先進先出。 Qt也提供了關聯(lián)式容器: QMap, QMultiMap, QHash, QMultiHash,and QSet. 。Multi容器支持多個value關聯(lián)到單一的key,。Hash容器提供了通過hash函數(shù)快速查找取代二分查找,。 特殊情況下,, QCache 和 QContiguousCache 提供了在有限的cache 中高效的查找對象。
容器可以嵌套,。例如,,當key的類型是Qstring和value類型是Qlist<int>,,完全可以用 QMap<QString, QList<int>>,。缺陷就是必須在> >之間插入空格,。否則C++編譯器會錯把> >,翻譯成右移位操作并報告語法錯誤。 這些容器被定義在不同的頭文件。為了方便,這些容器在文件<QtContainerFwd>.進行了前置聲明,。 容器保存的類型可以是任意可賦值的數(shù)據類型。因此要用容器存儲,,一個類型必須提供默認構造函數(shù),拷貝構造函數(shù)和賦值操作,。這包含了大部分可能用容器存儲的數(shù)據類型,包括基本類型如int和double,,指針和Qt數(shù)據類型如Qstring,,Qdate和Qtime,。但是不包含QObject以及任意QObject的子類,。如果你視圖實例化QList<QWidget>,,編譯器將抱怨QWidget的拷貝構造函數(shù)和賦值操作是不可用的。如果你想用容器存儲這些對象,,可以以指針的方式進行保存,如QList<QWidget *>.,。 以下是滿足可賦值的自定義數(shù)據類型的例子: class Employee { public: Employee() {} Employee(const Employee &other); Employee &operator=(const Employee &other); private: QStringmyName; QDatemyDateOfBirth; }; 如果我們不提供拷貝構造函數(shù)或賦值操作,,C++將提供默認的實現(xiàn)成員拷貝。在以上的例子,,這么寫就足夠了,。如果你沒有提供任何構造函數(shù),,C++將提供默認構造函數(shù),,用成員的默認構造函數(shù)進行成員初始化,。即使沒有提供任何顯示的構造函數(shù)或賦值操作,下面的數(shù)據類型也可以用容器存儲,。 struct Movie { int id; QStringtitle; QDatereleaseDate; }; 一些容器對于要進行存儲的數(shù)據類型有額外的要求,,例如,QMap<Key,T>的key類型必須提供operator<(),。這樣的特殊要求在類的詳細描述中都有介紹,。在一些情況下,特殊的函數(shù)有特殊的要求,,這些只是每個函數(shù)的基礎描述,,當要求不滿足時編譯器將報錯。 Qt容器提供operator<<() 和operator>>() 所以可以很容易用 QDataStream進行讀寫,。這意味著存儲在容器中的數(shù)據類型也必須支持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ù)的文檔說明設計默認構造值,例如,, QVector用默認構造值自動的初始化其元素,。如果沒有指定key值,則QMap::value() 返回默認構造值,。對于大多數(shù)值類型,,這意味著簡單的用默認構造函數(shù)創(chuàng)建默認值。但是對于原始類型如int,double和指針,,C++語言不指定任何初始化,,在這種情況下,Qt容器自動的初始化為0. The Iterator Classes迭代器提供了訪問容器元素的統(tǒng)一方法,。Qt容器提供兩種迭代器:Java-style iterators and STL-style iterators.,。當容器中的數(shù)據被修改或者由于調用非const成員函數(shù)生成隱式共享數(shù)據副本時,這兩種迭代器都會無效,。 Java-StyleIteratorsJava-style iterators是在Qt4才新加入的而且Qt程序中標準的使用著,。它們比STL-styleiterators用起來方便,但是效率稍微低了一點,。它們的API仿照java的迭代器類,。 對于美國容器類,有兩種Java-style iterator數(shù)據類型,,一種是只讀,,一種可讀寫: ContainersRead-only iteratorRead-write
在此討論中,我們只關注 QList 和 QMap,。 QLinkedList, QVector,和 QSet的迭代器跟Qlist的完全一樣,,同用的, QHash 的迭代器也跟 QMap的完全一樣,。 與STL-style iterators 不同的是, Java-style iterators指向元素的中間而不是直接指向元素的,。因此,它們指向容器的開頭,,容器的末尾或者兩個元素的之間,。如下圖所示: 以下是典型的遍歷 QList<QString>并輸出到控制臺的循環(huán): QList<QString>list; list << "A" << "B" << "C" <<"D"; QListIterator<QString>i(list); while (i.hasNext()) qDebug()<< i.next(); 它的工作原理如下:QList 的迭代傳遞到 QListIterator 的構造函數(shù)。此時,,迭代器指向list的第一個元素之前,。然后調用 hasNext() 檢測迭代器后面是否有元素,如果有,,我們調用next() 跳過該元素,,next() 返回所跳過的元素。對于 QList<QString>,其元素的類型是 QString.,。 下面是反向遍歷QList: QListIterator<QString>i(list); i.toBack(); while (i.hasPrevious()) qDebug()<< i.previous(); 這段代碼與正向遍歷的對稱,,除了開始調用toBack() 把迭代器移到最后元素的后面。 下圖說明了調用 next() 和 previous() 的效果: 下面總結了 QListIterator API:
QListIterator不提供插入或刪除元素的函數(shù),。要實現(xiàn)插入刪除,,可以用QMutableListIterator,下面例子從 QList<int> 中刪除奇書: QMutableListIterator<int> i(list); while (i.hasNext()) { if (i.next() % 2 != 0) i.remove(); } 循環(huán)中每次調用next()都跳過后面的元素,。remove() 函數(shù)從list中刪除最近跳過的元素。調用remove() 不會是迭代器失效,所以可以安全的跡象使用它,。反向遍歷也一樣: QMutableListIterator<int> i(list); i.toBack(); while (i.hasPrevious()) { if (i.previous() % 2 != 0) i.remove(); } 如果想改變存在的一個元素,,可以用setValue().。 QMutableListIterator<int> i(list); while (i.hasNext()) { if (i.next() > 128) i.setValue(128); } 和 remove()一樣,, setValue()作用于最近跳過的元素,。如果是正向遍歷,這個元素在迭代器的前面,,如果是反向遍歷,,該元素在迭代器的后面。 next() 返回一個元素的非const引用,,對于簡單的操作,,我們不需要setValue():: QMutableListIterator<int> i(list); while (i.hasNext()) i.next() *= 2; 如以上提到的,QLinkedList,,, QVector',,, 和 QSet的迭代器的API 跟 QList的一樣。現(xiàn)在來討論 QMapIterator,,它遍歷(key,,value)對,多少有些不同,。 像 QListIterator一樣,,, QMapIterator 提供toFront(), toBack(), hasNext(), next(), peekNext(), hasPrevious(), previous(),和 peekPrevious(). 。key和value通過next(),peekNext(), previous(), 或者 peekPrevious().的返回對象調用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-StyleIteratorsSTL-style iterators 從Qt2.0開發(fā)使用。它們與Qt的和STL的通用算法兼容,,并進行了速度優(yōu)化,。 對于每個容器,都有兩種STL-style iterator類型:一種是只讀,,一種是可讀寫的,。應該進可能的使用只讀的迭代器,因為它比可讀寫的迭代器快,。
STL iterators 的API參照數(shù)組的指針,。例如,++操作將迭代器指向下一個元素,,* 操作返貨迭代器指向的元素,。實際上,QVector 和 QStack,的元素存儲在相鄰的內存位置, iterator 僅僅是T *,的別名,, const_iterator 是 constT *.的別名,。 在此討論中,我們只關注 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()返回指向容器的第一個元素的迭代器。end() 返回一個迭代器指向超過容器最后一個元素的虛擬的元素,。end() 標記無效的位置,,不能解引用,典型的用于循環(huán)終止條件,。如果list是空的,,begin() 和 end(),相等,循環(huán)不會被執(zhí)行,。 下圖的紅色箭頭顯示的是有效的迭代器位置: 用STL-style iterator 進行反向迭代在訪問元素之前必須先自減,。 QList<QString>list; list << "A" << "B" << "C" <<"D"; QList<QString>::iteratori = list.end(); while (i != list.begin()) { --i; *i = (*i).toLower(); } 到目前為止的代碼中,我們用* 操作獲取元素的值,,然后調用了QString::toLower() ,。大部分的編譯器也支持 i->toLower(),,有的則不支持,。 對于只讀,,我們用const_iterator, constBegin(), 和 constEnd() QList<QString>::const_iteratori; for (i = list.constBegin(); i != list.constEnd(); ++i) qDebug()<< *i; 下表總結了STL-style iterators' API:
++和—操作都可以用做前綴和后綴。前綴修改迭代器并返回修改后的迭代器,,后綴版本先復制迭代器,,再修改,返回復制的副本,。當忽略返回值的表達式中,,我們建議使用前綴版本,因為它比較快一些,。 對于非const迭代器類型,,*操作的返回值可以用作復制操作的左值。 對于QMap 和 QHash, *操作返回元素的value部分,。如果你想獲得key,,可以對迭代器調用key()。對應的,,迭代器也提供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ù)返回容器代價不是很高。Qt的API包含了很多返回QList 或者 QStringList 的函數(shù),。如果你想用 STL 迭代器遍歷它們,,應該先拷貝容器并遍歷拷貝的容器副本: // 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的另外一個結果是:當容器正在使用非const迭代器時不能拷貝容器,, Java-style iterators不受此限制。 The foreach Keyword如果你只是想依次的遍歷容器的元素,,可以用Qt的關鍵字foreach ,,該關鍵字是Qt對C++額外添加的,用預處理器實現(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ù)據類型不包含逗號,,用于遍歷的變量可以聲明在foreach 之內: QLinkedList<QString>list; ... foreach(const QString&str, list) qDebug()<< str; 和其他C++循環(huán)結構一樣,你可以在foreach 循環(huán)內用分支,,可以可以用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,,可以用迭代器或者下如下代碼: ... 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; } 當進入foreach 循環(huán)時Qt自動拷貝容器,。如果你在遍歷的時候改變了容器,也不會影響循環(huán),。(即使你不改變容器,,也一樣會進行拷貝容器,多虧了隱式共享拷貝容器速度很快,。) 由于foreach 創(chuàng)建了容器的副本,,所以用非const變量并不能改變原始的容器,僅僅影響容器副本,,這可能不是你想要的,。 另外,Qt也提供了偽關鍵字forever 無限循環(huán): forever { ... } 如果你擔心命名空間受污染,,可以在 .pro文件中添加下面的代碼行禁止這些宏: CONFIG += no_keywords Other Container-LikeClassesQt包含了三個在某些方面類似容器的模板類,,這些類不提供迭代器,而且不能用foreach 關鍵字: · 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算法復雜度關心當容器的元素增加時每個函數(shù)的運行有多快,。例如,在QLinkedList中間插入一個元素速度是很快的,,不管里面存儲了多少個元素,。但是,如果在QVector 中插入一個元素,,如果QVector 包含有很多元素時代價可能會很大,,因為一般的元素都需要移動一個位置,。 為了描述算法復雜度,我們用以下基于big Oh的術語: · 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. 下表總結了Qt順序容器類的算法復雜度:
在表中,,"Amort." 代表"amortized behavior",。例如,"Amort.O(1)"意思是如果你只調用函數(shù)一次,,復雜度可能是O(n) ,,但是如果你多次調用函數(shù),平均復雜度則為O(1),。 下表總結了Qt關聯(lián)容器的算法復雜度:
QVector, QHash,和 QSet, ,,在后面追加元素的復雜度為O(log n).,通過調用QVector::reserve(), QHash::reserve(),或者 QSet::reserve() 可以降到O(1) ,。Growth StrategiesQVector<T>, QString,和 QByteArray 的元素存儲在連續(xù)的內存中,;QList<T>維護著一個指向其元素的指針數(shù)組以便快速索引元素(除非T是指針類型或者一個基本類型的指針的大小,此時值本身存在數(shù)組),。QHash<Key,T> 擁有一個hash表,,hash表的大小與其中的元素的個數(shù)成比例,要避免每次在容器后面添加元素時都進行內存重新分配,,這里類分配了多于實際需要的內存,。 考慮下面的代碼,我們從一個QString生成另一個QString: QStringonlyLetters(const QString&in) { QStringout; for (int j = 0; j < in.size(); ++j) { if (in[j].isLetter()) out += in[j]; } return out; } 我們通過每次動態(tài)的添加一個字符來創(chuàng)建一個字符串out,。假設我們添加15000個字符到QString字符串,。當QString用完內存之后進行了以下18此內存再分配:4, 8, 12, 16, 20, 52, 116, 244, 500, 1012, 2036, 4084, 6132, 8180,10228, 12276, 14324, 16372.。最后,,QString字符串分配了16372個Unicode 字符的空間,,其中15000個被占用。 上面的值看起來可能有點奇怪,,下面是指導原則: · QString 每次分配4個字符的空間知道其大小為20 · 從20到4080其大小每次成倍增加,,.更準確的說,它增加到下一個2的冪次方減去 12. · 從4080開始,,它每次增加2048個字符,。這很容易理解因為現(xiàn)代的操作系統(tǒng)當再分配內存是不拷貝整個數(shù)據,物理內存頁僅僅是被重新整理了,,而且只有第一個和最后一個也需要拷貝,。 QByteArray 和 QList<T> 用的算法有點像Qstring。 QVector<T> 對于可以用memcpy() 拷貝數(shù)據的也用那些算法,,但是對于通過調用拷貝構造函數(shù)和析構函數(shù)拷貝的數(shù)據類型則用不同的算法,。由于在那種情況下再分配內存的代價很高,當用完內存的時候QVector<T>通過成倍增長以減少分配次數(shù),。 QHash<Key, T>則是完全不一樣的情況,。Qhash的內部hash表以平方次增長的,,而且每次增長的元素被重定位到新的桶中,計算為 qHash(key) % QHash::capacity(),。這個備注也使用與 QSet<T> 和 QCache<Key, T> ,。 對于大部分程序而言,Qt默認提供的算法是起作用的,。如果你需要更多的控制,,QVector<T>, QHash<Key, T>, QSet<T>, QString, and QByteArray 提供了三個函數(shù)允許你檢查和指定需要用多少內存來存儲元素: · capacity() 返回分配的可以存儲元素的數(shù)量的內存。 · reserve(size) 顯示的預先分配size個元素的內存 · squeeze() 釋放不需要存儲元素的內存,。 · 如果你知道容器大概要存儲多少個元素,,你可以調用reserve(),,,而且當你完成填充容器,,你可以調用squeeze() 釋放多余的預先分配的內存。 http://blog.csdn.net/hai200501019/article/details/9247673 |
|