一、前言“合抱之木,生于毫末;九层之台,起于垒土;千里之行,始于足下”,上一章我们知道了如何使用Qt创建简单的示例程序,了解了最基本的Qt框架,在进一步学习Qt框架和消息机制前,我们应该对Qt本身有一个更细致的了解,这个了解就是Qt的模板库、工具类和控件。
回到顶部(go to top)
二、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
2 #include
3 #include
4 #include
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
2 QList
上述代码中,第一种是错误的。因为这些类(QObject及其他的子类)没有复制构造函数和赋值构造函数。解决的办法就是使用指向这些类的指针来作为存储类型。
另外的一点就是Qt的容器类是可以嵌套的,这一点C++的STL也是可以做到的。
1 QHase
Qt的容器类为遍历其中的内容提供了以下两种方法:
(1)Java风格的迭代器
(2)STL风格的迭代器,能够同Qt和STL的通用算法一起使用,并且在效率上也略胜一筹。
下面我们将通过具体的容器类来具体的了解Qt容器类的功能和使用方法。
2.2.1 QList类、QLinkedList类和QVector类首先我们来看一下这三种容器类的操作时间复杂度。
容器类查找(访问)插入头部添加尾部添加
QListO(1)O(n)Amort.O(1)Amort.O(1)
QLinkedListO(n)O(1)O(1)O(1)
QVectorO(1)O(n)O(n)Amort.O(1)
注:其中Amort.O(1)表示,如果仅完成一次操作,可能会有O(n)行为;但是如果完成多次操作(比如n次),平均结果将会是O(1)。
1、QList类
QList
QList
(1)如果T是一个指针类型或者指针大小的基本类型(就是说该基本类型占有的字节数和指针类型占有的字节数相同),QList
(2)如果QList
1 #include
2 #include
3 #include
4
5 int main(int argc, char *argv[])
6 {
7 QCoreApplication a(argc, argv);
8 QList
9 //{}用来表示作用域
10 {
11 QString str(this is a test string);
12 list
13 }
14 qDebug()
15 return a.exec();
16 }
其中list
(2)QLinkedList类
QLinkedList
(3)QVector类
QVector
QVector
注:其实平常我们用QList就可以,它既可以是存储指向存储类型的指针,也可以对小的数据进行直接存储。
附:STL风格迭代器遍历容器
对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。由于只读类型的迭代器的运行速度要比读写迭代器的运行速度快,建议尽可能使用只读类型的迭代器。
容器类只读迭代器类读写迭代器类
QList
QLinkedList
QVector
1 #include
2 #include
3 #include
4
5 int main(int argc, char *argv[])
6 {
7 QCoreApplication a(argc, argv);
8 QList
9 for(int j=0;j10;j++)
10 list.insert(list.end(),j);
11 QList
12 for(i=list.begin();i!=list.end();i++)
13 {
14 printf(%d ,*i);
15 *i=(*i)*10;
16 }
17 QList
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
2、QHash类
QHash
附:STL风格迭代器遍历容器
和上面说到的容器相同,Qt也提供了两种类型的STL风格迭代器数据类型。
容器类只读迭代器读写迭代器
QMap
QHase
1 #include
2 #include
3 #include
4 int main(int argc, char *argv[])
5 {
6 QCoreApplication a(argc, argv);
7 QMap
8 map.insert(lili,1990);
9 map.insert(wangli,1992);
10 map.insert(zhangli,1989);
11 QMap
12 for(i=map.constBegin();i!=map.constEnd();i++)
13 qDebug()
14 QMap
15 mi=map.find(lili);
16 if(mi!=map.end())
17 mi.value()=1995;
18 QMap
19 for(modi=map.constBegin();modi!=map.constEnd();modi++)
20 qDebug()
21 return a.exec();
22 }
好了,关于字符串类和容器类先说这么多,如有错误,欢迎指正。
no pains ,no gains. 给自己加油,为未来奋斗。