我的QT5學習之路(三)——模板庫、工具類和控件(上)


一、前言

  “合抱之木,生於毫末;九層之台,起於壘土;千里之行,始於足下”,上一章我們知道了如何使用Qt創建簡單的示例程序,了解了最基本的Qt框架,在進一步學習Qt框架和消息機制前,我們應該對Qt本身有一個更細致的了解,這個了解就是Qt的模板庫、工具類和控件。

二、Qt的模板庫、工具類和控件

2.1 字符串類

1、字符串的操作

  我們了解字符串的操作就是要了解字符串主要有哪些操作符,Qt基於C++繼承和強化了string的功能,結構類型為QString,QString提供了一個二元的“+”和“+=”操作符,其中“+=”操作符功能和append函數方法具有同樣的功能,是現在一個字符串末尾追加另一個字符串,學習時可基於C++中String類進行比較。

1 QString str1="nihao";
2 QString str2="Qt";
3 
4 str1+=str2;      //str1="nihaoQt"
5 str1=str1+str2;  //str1="nihaoQtQt"
6 str1.append(str2); //str1="nihaoQtQtQt"
7 str1.append("yes"); //str1="nihaoQtQtQtyes"

  Qt組合字符串的另一個函數楇 QString::sprintf(),此函數支持的格式定義符和C++庫中的函數sprintf定義的一樣。Qt還提供了另外一種方便的字符串組合方式,使用QString::arg()函數,此函數的重載可以處理多種數據類型,一些重載具有額外的參數對字段的寬度、數字基數或者浮點數精度進行控制。相對於sprintf來說,srg是一個比較好的解決方案,因為它類型安全,完全支持Unicode,並且允許改變"%n"參數的順序。

1 QString str;
2 str=QString("%1 was born in %2 .").arg("Rimond").arg(1990);
3 //str="Rimond was born in 1990".

  此外,QString也提供了一些其他組合字符串的方法

函數名稱 函數功能
insert() 在原字符串特定的位置插入另一個字符串
prepend() 在原字符串的開頭插入另一個字符串
replace() 用指定的字符串代替原字符串中的某些字符

 

  為了解決特定場景比如去除一個字符串兩端的空白(空白字符包括回車字符“\n”,換行字符“\r”,制表符“\t”和空格字符等),QString提供了特定的函數。

函數名稱 函數功能
trimmed()        移出字符串兩端的空白字符
simplified() 移除字符串兩端的空白字符,使用單個空格字符“ ”代替字符串中出現的空白字符

 

1 QString str="  Hello \t  QT \n ! ";
2 str=str.trimmed();
3 
4 //str=" Hello \t to \n you! "
5 //如果使用str=str.simplified(),str的結果是“Hello Qt !”

2、查詢字符串數據

  查詢字符串數據有多種樣式。

  (1) QString::startsWith()判斷一個字符串是否以某個字符串開頭。此函數具有兩個參數,第一個參數指定了一個字符串,第二個參數指定是否大小寫敏感(默認大小寫敏感)。

1 QString str="Hello Qt!";
2 str.startsWith("Hello",Qt::CaseSensitive); //返回真
3 str.startsWith("Qt",Qt::caseSenstive); //返回假

 

  (2) QString::endwith()類似於QString::startswith(),它用來判斷一個字符串是否以某個字符串結尾。

  (3) QString::contains()判斷一個指定的字符串是否出現過。

1 QString str ="Hello QT!";
2 str.contains("QT",Qt::CaseSensitive);   //返回真

  (4) QString類還重載了多種用於比較的操作符,用法可參照C++ string類中重載的比較操作符。此外,QString類增加了兩個特殊函數。

  localeAwareCompare(const QString&,const QString&):靜態函數,比較前后兩個字符串,如果前面字符串小於后面字符串,則返回值為負整數;如果等於則返回0;如果大於則返回值為正整數,該函數用於比較基於本地字符集,而且楇平台相關的,通常該函數用於向用戶顯示一個有序的字符串列表。

  compare(const QString&,const QString&::CaseSensitivity):該函數可以指定是否進行大小寫的比較,而大小寫的比較楇完全基於字符的Unicode編碼值的,而且是非常快的,返回值類似於localeAwareCompare函數。

3、字符串的轉換

  由於Qt的跨平台型,可移植性等特點反映了其在字符串上的靈活性,QString類提供了豐富的轉換函數,可以實現講一個字符串轉換為數值類型或者其他的字符編碼集。

  (1) QString::toInt()函數實現了將字符串轉換為整型數值,類似的函數還有toDouble()、toFloat()、toLong()、toLongLong()等。

  

1 QString str="125";
2 bool ok;
3 int hex=str.toInt(&ok,16);    //ok=true,hex=293
4 int dec=str.toInt(&ok,10);    //ok=true,dec=125

 可以看到上面的16和10分別代表了進制,ok用於傳遞一個地址,表示轉換結果。

  (2) QString提供的字符串編碼集的轉換函數將會返回一個const char*類型版本的QByteArry,即構造函數QByteArry(const char*)構造的QByteArry對象。QByteArry類具有一個字符數組,它既可以存儲原始字節(raw bytes),也可以存儲傳統的以“\0”結尾的8位的字符串。在Qt中,使用QByteArry比使用const char*更為方便,且QByteArry也支持隱式共享,轉換函數有以下幾種。

函數名稱 函數功能
toAscii()                                             返回一個ASCII編碼的8位字符串
toLatin1() 返回一個Latin-1編碼的8位字符串
toUtf8() 返回一個utf-8編碼的8位字符串(utf-8是ASCII碼的超集,它支持整個Unicode字符集)
toLocal8Bit() 返回一個系統本地編碼的8位字符串

 

 1 #include <QCoreApplication>
 2 #include <QByteArray>
 3 #include <QString>
 4 #include <QtDebug>
 5 
 6 int main(int argc, char *argv[])
 7 {
 8     QCoreApplication a(argc, argv);
 9     QString str="Hello Qt!";
10     QByteArray ba=str.toLatin1();
11     qDebug() << ba;
12     ba.append("Hello,world!");
13     qDebug() << ba.data();
14     return a.exec();
15 }

運行結果如下

提示:Qt5中去除了toAscii()函數,改用toLatin1()函數就可了,再就是debug環境下別忘了添加qDebug的頭文件。

     qDebug類似於cout格式輸出。

附加:NULL字符串和空(empty)字符串的區別

  一個NULL字符串就是使用QString的默認構造函數或者使用(const char*)0作為參數的構造函數創建的QString字符串對象;而一個空字符串是一個大小為0的字符串。一個NULL字符串一定是一個空字符串,而一個空字符串未必是一個NULL字符串。

  驗證方式  

1 QString().isNull();   //true
2 QString().isEmpty(); //true
3 QString("").isNull();   //false
4 QString("").isEmpty(); //true

 

 

2.2 容器類

  同C++的標准模板庫中的容器類作比較,Qt提供了一組通用的基於模板的容器類。這些容器更輕量、更安全並且更容易使用,同時還在速度、內存消耗和內聯等方面進行了優化。

  對於Qt容器中的存儲數據類型也有要求,這些數據必須是可以賦值的數據類型,換句話說就是該數據類型必須有一個默認的構造函數(無參數構造函數)、一個復制構造函數(拷貝構造)和一個賦值操作符函數。

  其實這樣的數據類型包含了通常我們使用的大多數數據類型,比如基本的數據類型(int和double等)和Qt的一些數據類型(如QString、QDate、QTime等)。但是,Qt的QObject及其他的子類(如QWidget和Qdialog等)是不可以存儲在容器中的。

1 QList<QToolBar> list;     //error
2 QList<QToolBar*>list;   //ok

  上述代碼中,第一種是錯誤的。因為這些類(QObject及其他的子類)沒有復制構造函數和賦值構造函數。解決的辦法就是使用指向這些類的指針來作為存儲類型。

  另外的一點就是Qt的容器類是可以嵌套的,這一點C++的STL也是可以做到的。

1 QHase<QString,QList<double> > ;

  Qt的容器類為遍歷其中的內容提供了以下兩種方法:

  (1)Java風格的迭代器

  (2)STL風格的迭代器,能夠同Qt和STL的通用算法一起使用,並且在效率上也略勝一籌。

  下面我們將通過具體的容器類來具體的了解Qt容器類的功能和使用方法。

2.2.1  QList類、QLinkedList類和QVector類

  首先我們來看一下這三種容器類的操作時間復雜度。

容器類 查找(訪問) 插入 頭部添加 尾部添加
QList O(1) O(n) Amort.O(1) Amort.O(1)
QLinkedList O(n) O(1) O(1) O(1)
QVector O(1) O(n) O(n) Amort.O(1)

 

  注:其中Amort.O(1)表示,如果僅完成一次操作,可能會有O(n)行為;但是如果完成多次操作(比如n次),平均結果將會是O(1)。

 1、QList類

  QList<T>是我們會經常使用的容器類,它存儲給定數據類型T的一列數值。繼承自QList類的子類有QItemSelection、QQueue、QSignalSpy及QStringList和QTestEvenList。QList不僅提供了可以在列表進行追加的QList::append()和QList::prepend()函數,還提供了在列表中間完成插入操作的函數QList::insert()。相對於任何其他的Qt容器類,為了使可執行代碼盡可能的少,QList被高度優化。

  QList<T>維護了一個指針數組,該數組存儲的指針指向QList<T>存儲的列表項的內容。因此,QList<T>提供了基於下標的快速訪問。對於不同的數據類型,QList<T>采用不同的存儲策略,主要有以下幾種。

  (1)如果T是一個指針類型或者指針大小的基本類型(就是說該基本類型占有的字節數和指針類型占有的字節數相同),QList<T>會將數值直接存儲在它的數組中。

  (2)如果QList<T>存儲對象的指針,則該指針指向實際存儲的對象。

 1 #include <QCoreApplication>
 2 #include <QString>
 3 #include <QtDebug>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     QCoreApplication a(argc, argv);
 8     QList<QString> list;
 9     //{}用來表示作用域
10     {
11         QString str("this is a test string");
12         list<<str;
13     }
14     qDebug()<<list[0]<<"Hello Qt!";
15     return a.exec();
16 }

其中list<<str;中的<<是通過<<操作符將一個QString字符串存儲在該列表中。

  (2)QLinkedList類

  QLinkedList<T>是一個鏈式列表,它以非連續的內存塊保存數據。QLinkedList<T>不能使用下標,只能使用迭代器訪問他的數據項。與QList相比,當對一個很大的列表進行插入操作時,QLinkedList具有更高的效率。

  (3)QVector類

  QVector<T>在相鄰的內存中存儲給定數據類型T的一組數值。在一個QVector的前部或者中間位置進行插入操作的速度是很慢的,這是因為這樣的操作將導致內存中大量的數據被移動,這事有QVector存儲數據的方式決定的。

  QVector<T>既可以使用下標訪問數據項,也可以使用迭代器訪問數據項。繼承自QVector類的子類有QPolygon、QPolygonF、QStack。

  注:其實平常我們用QList就可以,它既可以是存儲指向存儲類型的指針,也可以對小的數據進行直接存儲。

  附:STL風格迭代器遍歷容器

  對於每一個容器類,Qt都提供了兩種類型的STL風格迭代器數據類型:一種提供只讀訪問;另一種提供讀寫訪問。由於只讀類型的迭代器的運行速度要比讀寫迭代器的運行速度快,建議盡可能使用只讀類型的迭代器。

容器類 只讀迭代器類 讀寫迭代器類
QList<T>,QQueue<T> QList<T>::const_itertor QList<T>::itertor
QLinkedList<T> QLinkedList<T>::const_itertor QLinkedList<T>::itertor
QVector<T>,QStack<T> QVector<T>::const_itertor

QVector<T>::itertor

 

 1 #include <QCoreApplication>
 2 #include <QString>
 3 #include <QtDebug>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     QCoreApplication a(argc, argv);
 8     QList<int> list;
 9     for(int j=0;j<10;j++)
10         list.insert(list.end(),j);
11     QList<int>::iterator i;
12     for(i=list.begin();i!=list.end();i++)
13     {
14         printf("%d ",*i);
15         *i=(*i)*10;
16     }
17     QList<int>::const_iterator ci;
18     for(ci=list.constBegin();ci!=list.constEnd();ci++)
19        printf("%d ",*ci);
20     return a.exec();
21 }

 

 

注:如果使用qDebug輸出的話,我認為每次迭代都會生成一個qDebug的臨時對象,並且自動添加換行,所以我改成了printf(偷懶),另外關於容器類使用方法可以參考C++ STL的使用方法。

2.2.2 QMap類和QHash類

  QMap類和QHash類具有非常類似的功能,他們的差別僅在於:

  1、QHash具有比QMap更快的查找速度

  2、QHash以任意順序存儲數據項,而QMap總是按照Key順序存儲數據;

  3、QHash的鍵類型Key必須提供operator==()和一個全局的qHash(Key)函數,而QMap的鍵類型Key必須提供operator<()函數。

QMap和QHash的時間復雜度比較

                                                                                              

 

1、QMap類

  QMap<key,T>提供了一個從類型Key的鍵到類型為T的值的映射。通常,QMap存儲的數據類型是一個鍵對應一個值,並且按照鍵Key的次序存儲數據。為了能夠支持一鍵多值的情況,QMap提供了QMap<key,T>::insertMulti()和QMap<key,T>::values()函數。存儲一鍵多值的數據時,也可以使用QMultiMap<key,T>容器,他繼承自QMap。

2、QHash類

  QHash<key,T>具有與QMap幾乎完全相同的API。QHash維護一張哈希表,哈希表的大小與QHash的數據項的數目相適應。QHash作為存放數據的容器。QHash也可以存儲一鍵多值形式的數據,他的子類QMultiHash<key,T>實現了一鍵多值的語義。

附:STL風格迭代器遍歷容器

  和上面說到的容器相同,Qt也提供了兩種類型的STL風格迭代器數據類型。

容器類 只讀迭代器 讀寫迭代器
QMap<key,T>,QMultiMap<key,T> QMap<key,T>::const_itertor QMap<key,T>::itertor
QHase<key,T>,QMultiHase<key,T> QHash<key,T>::const_itertor QHash<key,T>::itertor

 

 1 #include <QCoreApplication>
 2 #include <QString>
 3 #include <QtDebug>
 4 int main(int argc, char *argv[])
 5 {
 6     QCoreApplication a(argc, argv);
 7     QMap<QString,QString>map;
 8     map.insert("lili","1990");
 9     map.insert("wangli","1992");
10     map.insert("zhangli","1989");
11     QMap<QString,QString>::const_iterator i;
12     for(i=map.constBegin();i!=map.constEnd();i++)
13         qDebug()<<" "<<i.key()<<" "<<i.value();
14     QMap<QString,QString>::iterator mi;
15     mi=map.find("lili");
16     if(mi!=map.end())
17         mi.value()="1995";
18     QMap<QString,QString>::const_iterator modi;
19     for(modi=map.constBegin();modi!=map.constEnd();modi++)
20        qDebug()<<" "<<modi.key()<<" "<<modi.value();
21     return a.exec();
22 }

  好了,關於字符串類和容器類先說這么多,如有錯誤,歡迎指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM