第2章 Qt 5模板库、工具类及控件 |
|
|
第2章Qt5模板库、工具类及控件——字符串类01操作字符串操作字符串字符串有如下几个操作符。(1)QString提供了一 个二元的“+”操作符用于组合两个字符串,并提供了一个“+=”操作符用于将一个字符串追加到另一个字符串的末尾,例如:QString str1="Welcome";str1=str1+"toyou!"; //str1="Welcometoyou! "QStringstr2="Hello,";str2+="World!"; //str2="Hello,World!"其中 ,QStringstr1="Welcome"传递给QString一个constchar类型的ASCII字符串“Wel come”,它被解释为一个典型的以“\0”结尾的C类型字符串。这将会导致调用QString构造函数,来初始化一个QString字符 串。其构造函数原型为:QT_ASCII_CAST_WARN_CONSTRUCTORQString::QString(const charstr)被传递的constchar类型的指针又将被QString::fromAscii()函数转换为Unicode 编码。默认情况下,QString::fromAscii()函数会将超过128的字符作为Latin-1进行处理(可以通过调用QTex tCodec::setCodecForCString()函数改变QString::fromAscii()函数的处理方式)。操作 字符串(2)QString::append()函数具有与“+=”操作符同样的功能,实现在一个字符串的末尾追加另一个字符串,例如 :QStringstr1="Welcome";QStringstr2="to";str1.append(str2) ;//str1="Welcometo"str1.append("you!");//str1="Welcometoyou! "(3)组合字符串的另一个函数是QString::sprintf(),此函数支持的格式定义符和C++库中的函数sprintf() 定义的一样。例如:QStringstr;str.sprintf("%s","Welcome"); //str="Welcom e"str.sprintf("%s","toyou!"); //str="toyou!"str.sprintf("%s %s","Welcome","toyou!"); //str="Welcometoyou!"操作字符 串(4)Qt还提供了另一种方便的字符串组合方式,使用QString::arg()函数,此函数的重载可以处理很多的数据类型。此外,一 些重载具有额外的参数对字段的宽度、数字基数或者浮点数精度进行控制。通常,相对于QString::sprintf()函数,QStri ng::arg()函数是一个比较好的解决方案,因为其类型安全,完全支持Unicode,并且允许改变"%n"参数的顺序。例如:QSt ringstr;str=QString("%1wasbornin%2.").arg("John").arg(1998); //str="Johnwasbornin1998."其中,"%1"被替换为"John","%2"被替换为"1998"。(5 )QString也提供了一些其他组合字符串的方法,包括如下几种。①insert()函数:在原字符串特定的位置插入另一个字符串。② prepend()函数:在原字符串的开头插入另一个字符串。③replace()函数:用指定的字符串代替原字符串中的某些字符。操 作字符串(6)很多时候,去掉一个字符串两端的空白(空白字符包括回车字符“\n”、换行字符“\r”、制表符“\t”和空格字符 “”等)非常有用,如获取用户输入的账号时。①QString::trimmed()函数:移除字符串两端的空白字符。②QStri ng::simplified()函数:移除字符串两端的空白字符,使用单个空格字符“”代替字符串中出现的空白字符。例如:QStri ngstr="Welcome\tto\nyou!";str=str.trimmed(); //str="Welcome\tto\nyou!"02查询字符串数据查询字符串数据(1) QString::startsWith()函数判断一个字符串是否以某个字符串开头。此函数具有两个参数。第一个参数指定了一个字符串, 第二个参数指定是否大小写敏感(默认情况下,是大小写敏感的),例如:QStringstr="Welcometoyou!";s tr.startsWith("Welcome",Qt::CaseSensitive); //返回truestr.startsW ith("you",Qt::CaseSensitive); //返回false(2)QString::endsWith()函数类似 于QString::startsWith()函数,此函数判断一个字符串是否以某个字符串结尾。(3)QString::contain s()函数判断一个指定的字符串是否出现过,例如:QStringstr="Welcometoyou!";str.conta ins("Welcome",Qt::CaseSensitive); //返回true查询字符串数据(4)比较两个字符串也是经常使用 的功能,QString提供了多种比较手段。①operator<(constQString&):比较一个字符串是否小于另一个字符 串。如果是,则返回true。②operator<=(constQString&):比较一个字符串是否小于等于另一个字符串。如果 是,则返回true。③operator==(constQString&):比较两个字符串是否相等。如果相等,则返回true。④ operator>=(constQString&):比较一个字符串是否大于等于另一个字符串。如果是,则返回true。⑤loc aleAwareCompare(constQString&,constQString&):静态函数,比较前后两个字符串。如果前 面字符串小于后面字符串,则返回负整数值;如果等于则返回0;如果大于则返回正整数值。该函数的比较是基于本地(locale)字符集的, 而且是与平台相关的。通常,该函数用于向用户显示一个有序的字符串列表。⑥compare(constQString&,const QString&,Qt::CaseSensitivity):该函数可以指定是否进行大小写的比较,而大小写的比较是完全基于字符的Un icode编码值的,而且是非常快的,返回值类似于localeAwareCompare()函数。03字符串的转换字符串的转换QStr ing类提供了丰富的转换函数,可以将一个字符串转换为数值类型或者其他的字符编码集。(1)QString::toInt()函数将字符 串转换为整型数值,类似的函数还有toDouble()、toFloat()、toLong()、toLongLong()等。下面举个例 子说明其用法:QStringstr="125"; //初始化一个"125"的字符串boolok;inthex=str .toInt(&ok,16); //ok=true,hex=293intdec=str.toInt(&ok,10); //ok= true,dec=125其中,inthex=str.toInt(&ok,16):调用QString::toInt()函数将字符串 转换为整型数值。QString::toInt()函数有两个参数。第一个参数是一个bool类型的指针,用于返回转换的状态。当转换成功 时设置为true,否则设置为false。第二个参数指定了转换的基数。字符串的转换(2)QString提供的字符编码集的转换函数将会 返回一个constchar类型版本的QByteArray,即构造函数QByteArray(constchar)构造的QBy teArray对象。QByteArray类具有一个字节数组,它既可以存储原始字节(rawbytes),也可以存储传统的以“\0” 结尾的8位的字符串。在Qt中,使用QByteArray比使用constchar更方便,且QByteArray也支持隐式共享。转 换函数有以下几种。①toAscii():返回一个ASCII编码的8位字符串。②toLatin1():返回一个Latin-1(I SO8859-1)编码的8位字符串。③toUtf8():返回一个UTF-8编码的8位字符串(UTF-8是ASCII码的超集,它支 持整个Unicode字符集)。④toLocal8Bit():返回一个系统本地(locale)编码的8位字符串。字符串的转换下面举 例说明其用法:QStringstr="Welcometoyou!"; //初始化一个字符串对象QByteArrayb a=str.toAscii(); //(a)qDebug()<); //(c)qDebug()<.toAscii():通过QString::toAscii()函数,将Unicode编码的字符串转换为ASCII码的字符串,并存储 在QByteArray对象ba中。(b)qDebug()<数支持输出Qt对象)。(c)ba.append("Hello,World!"):使用QByteArray::append()函数 追加一个字符串。字符串的转换一个NULL字符串就是使用QString的默认构造函数或者使用“(constchar)0”作为参数 的构造函数创建的QString字符串对象;而一个空字符串是一个大小为0的字符串。一个NULL字符串一定是一个空字符串,而一个空字符 串未必是一个NULL字符串。例如:QString().isNull(); //结果为trueQString().isEmpty() ; //结果为trueQString("").isNull(); //结果为falseQString("").isEmpty(); //结果为true第2章Qt5模板库、工具类及控件——容器类容器类这样的数据类型包含了通常使用的大多数数据类型,包括 基本数据类型(如int和double等)和Qt的一些数据类型(如QString、QDate和QTime等)。不过,Qt的QObje ct及其他的子类(如QWidget和Qdialog等)是不能够存储在容器中的,例如:QListlist;上 述代码是无法通过编译的,因为这些类(QObject及其他的子类)没有复制构造函数和赋值操作运算符。一个可代替的方案是存储QObje ct及其子类的指针,例如:QListlist;Qt的容器类是可以嵌套的,例如:QHashg,QList>其中,QHash的键类型是QString,它的值类型是QList。Qt的容器类 为遍历其中的内容提供了以下两种方法。(1)Java风格的迭代器(Java-styleiterators)。(2)STL风格的迭代 器(STL-styleiterators),能够同Qt和STL的通用算法一起使用,并且在效率上也略胜一筹。1.QList类012 .QLinkedList类3.QVector类QList类、QLinkedList类和QVector类4.Java风格迭代器遍历容 器5.STL风格迭代器遍历容器QList类、QLinkedList类和QVector类在开发一个较高性能需求的应用程序时,程序员会 比较关注这些容器类的运行效率。表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)1.QList类QList是迄今为止最常 用的容器类,它存储给定数据类型T的一列数值。继承自QList类的子类有QItemSelection、QQueue、QSignalS py、QStringList和QTestEventList。QList不仅提供了可以在列表中进行追加的QList::append( )和Qlist::prepend()函数,还提供了在列表中间完成插入操作的QList::insert()函数。相对于任何其他的Qt 容器类,为了使可执行代码尽可能少,QList被高度优化。QList维护了一个指针数组,该数组存储的指针指向QList存 储的列表项的内容。因此,QList提供了基于下标的快速访问。对于不同的数据类型,QList采取不同的存储策略,存储策略 有以下几种。(1)如果T是一个指针类型或指针大小的基本类型(即该基本类型占有的字节数和指针类型占有的字节数相同),QList 会将数值直接存储在它的数组中。(2)如果QList存储对象的指针,则该指针指向实际存储的对象。1.QList类下面举一个例子 :#includeintmain(intargc,charargv[]){ QList list; //(a) { QStringstr("Thisisateststring"); list<< str;//(b) }//(c) qDebug()<中,(a)QListlist:声明了一个QList栈对象。(b)list<操作运算符“<<”将一个QString字符串存储在该列表中。(c)程序中使用花括弧“{”和“}”括起来的作用域表明,此时QLis t保存了对象的一个复制。2.QLinkedList类QLinkedList是一个链式列表,它以非连续的内存块保存数据。 QLinkedList不能使用下标,只能使用迭代器访问它的数据项。与QList相比,当对一个很大的列表进行插入操作时,QLi nkedList具有更高的效率。3.QVector类QVector在相邻的内存中存储给定数据类型T的一组数值。在一个QVec tor的前部或者中间位置进行插入操作的速度是很慢的,这是因为这样的操作将导致内存中的大量数据被移动,这是由QVector存储数据的 方式决定的。QVector既可以使用下标访问数据项,也可以使用迭代器访问数据项。继承自QVector类的子类有QPolygo n、QPolygonF和QStack。4.Java风格迭代器遍历容器Java风格迭代器同STL风格迭代器相比,使用起来更简单方便, 不过这也是以轻微的性能损耗为代价的。对于每一个容器类,Qt提供了两种类型的Java风格迭代器数据类型,即只读迭代器类和读写迭代器类 ,见表2.2。容器类只读迭代器类读写迭代器类QList,QQueueQListIteratorQMutabl eListIteratorQLinkedListQLinkedListIteratorQMutableLinke dListIteratorQVector,QStackQVectorIteratorQMutableVec torIteratorJava风格迭代器的迭代点(Java-styleiteratorspoint)位于列表项的中间,而 不是直接指向某个列表项。4.Java风格迭代器遍历容器下面以QList为例,介绍两种Java风格迭代器的用法。QLinkedLis t和QVector具有与QList相同的遍历接口,在此不再详细讲解。(1)QList只读遍历方法。【例】(简单)(CH201)通过 控制台程序实现QList只读遍历方法。其具体代码如下:#include #include< QDebug> //(a)intmain(intargc,charargv[]){QCoreApplicationa (argc,argv); //(b)QListlist;//创建一个QList栈对象listlis t<<1<<2<<3<<4<<5; //用操作运算符“<<”输入五个整数QListIteratori(list ); //(c)for(;i.hasNext();) //(d)qDebug()<a.exec();}4.Java风格迭代器遍历容器其中,(a)?头文件中已经包含了QList的头文件。(b)Qt的 一些类,如QString、QList等,不需要QCoreApplication的支持也能够工作,但是,在使用Qt编写应用程序时,如 果是控制台应用程序,则建议初始化一个QCoreApplication对象,Qt5.11创建控制台项目时生成的main.cpp源文 件中默认创建了一个QCoreApplication对象;如果是GUI图形用户界面程序,则会初始化一个QApplication对象。 (c)?QListIteratori(list):以该list为参数初始化一个QListIterator对象i。此时, 迭代点处在第一个列表项“1”的前面(注意,并不是指向该列表项)。(d)?for(;i.hasNext();):调用QListIte rator::hasNext()函数检查当前迭代点之后是否有列表项。如果有,则调用QListIterator::nex t()函数进行遍历。next()函数将会跳过下一个列表项(即迭代点将位于第一个列表项和第二个列表项之间),并返回它跳过的列表项的内 容。最后程序的运行结果为:123454.Java风格迭代器遍历容器上例是QListIterator对列表进行向后遍历 的函数,而对列表进行向前遍历的函数有如下几种。QListIterator::toBack():将迭代点移动到最后一个列表项的 后面。QListIterator::hasPrevious():检查当前迭代点之前是否具有列表项。QListIterator ::previous():返回前一个列表项的内容并将迭代点移动到前一个列表项之前。除此之外,QListIterator 提供的其他函数还有如下几种。toFront():移动迭代点到列表的前端(第一个列表项的前面)。peekNext():返回下一个列表 项,但不移动迭代点。peekPrevious():返回前一个列表项,但不移动迭代点。findNext():从当前迭代点开始向后查找 指定的列表项,如果找到,则返回true,此时迭代点位于匹配列表项的后面;如果没有找到,则返回false,此时迭代点位于列表的后端( 最后一个列表项的后面)。findPrevious():与findNext()类似,不同的是,它的方向是向前的,查找操作完成后的迭代 点在匹配项的前面或整个列表的前端。4.Java风格迭代器遍历容器(2)QListIterator是只读迭代器,它不能完成列表 项的插入和删除操作。读写迭代器QMutableListIterator除提供基本的遍历操作(与QListIterator的操 作相同)外,还提供了insert()插入操作函数、remove()删除操作函数和修改数据函数等。【例】(简单)(CH202)通过控 制台程序实现QList读写遍历方法。具体代码如下:#include#includebug>intmain(intargc,charargv[]){ QCoreApplicationa(argc,arg v); QListlist;//创建一个空的列表list QMutableListIteratori(li st); //创建上述列表的读写迭代器 for(intj=0;j<10;++j) i.insert(j);//(a) for (i.toFront();i.hasNext();) //(b) qDebug()<ck();i.hasPrevious();) //(c) { if(i.previous()%2==0)?i.remove() ; elsei.setValue(i.peekNext()10); //(d) } for(i.toFront();i.h asNext();) //重新遍历并输出列表qDebug()<ava风格迭代器遍历容器其中,(a)i.insert(j):通过QMutableListIterator::insert( )插入操作,为该列表插入10个整数值。(b)for(i.toFront();i.hasNext();)、qDebug()<next():将迭代器的迭代点移动到列表的前端,完成对列表的遍历和输出。(c)for(i.toBack();i.hasPrevi ous();){…}:移动迭代器的迭代点到列表的后端,对列表进行遍历。如果前一个列表项的值为偶数,则将该列表项删除;否则,将该列表 项的值修改为原来的10倍。(d)i.setValue(i.peekNext()10):函数QMutableListIterat or::setValue()修改遍历函数next()、previous()、findNext()和findPrevious( )跳过的列表项的值,但不会移动迭代点的位置。对于findNext()和findPrevious()有些特殊:当findNext() (或findPrevious())查找到列表项的时候,setValue()将会修改匹配的列表项;如果没有找到,则对setValue ()的调用将不会进行任何修改。最后编译,运行此程序,程序运行结果如下:012345678910305070 905.STL风格迭代器遍历容器对于每个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写 访问。由于只读类型的迭代器的运行速度要比读写迭代器的运行速度快,所以应尽可能地使用只读类型的迭代器。STL风格迭代器的两种分类见表 2.3。STL风格迭代器的API是建立在指针操作基础上的。例如,“++”操作运算符移动迭代器到下一个项(item),而“”操作运 算符返回迭代器指向的项。容器类只读迭代器类读写迭代器类QList,QQueueQList::const_it eratorQList::iteratorQLinkedListQLinkedList::const_itera torQLinkedList::iteratorQVector,QStackQVector::const_ iteratorQVector::iterator5.STL风格迭代器遍历容器【例】(简单)(CH203)使用STL风格迭代 器。具体代码如下:#include#includeintmain(int argc,charargv[]){ QCoreApplicationa(argc,argv); QListl ist;//初始化一个空的QList列表 for(intj=0;j<10;j++) list.insert(list. end(),j); //(a) QList::iteratori; //初始化一个QList::itera tor读写迭代器 for(i=list.begin();i!=list.end();++i) //(b) {qDebug()< <(i);i=(i)10; } //初始化一个QList::const_iterator读写迭代器 QLis t::const_iteratorci; //在控制台输出列表的所有值 for(ci=list.constBegin( );ci!=list.constEnd();++ci)qDebug()<风格迭代器遍历容器其中,(a)list.insert(list.end(),j):使用QList::insert()函数插 入10个整数值。此函数有两个参数:第一个参数是QList::iterator类型,表示在该列表项之前插入一个新的列表项(使用 QList::end()函数返回的迭代器,表示在列表的最后插入一个列表项);第二个参数指定了需要插入的值。(b)for(i =list.begin();i!=list.end();++i){…}:在控制台输出列表的同时将列表的所有值增大10倍。这里用到两 个函数:QList::begin()函数返回指向第一个列表项的迭代器;QList::end()函数返回一个容器最后列表 项之后的虚拟列表项,为标记无效位置的迭代器,用于判断是否到达容器的底部。最后编译、运行此应用程序,输出结果如下:012 345678901020304050607080901 .QMap类022.QHash类3.Java风格迭代器遍历容器QMap类和QHash类4.STL风格迭代器遍历容器QMap类和QH ash类QMap类和QHash类具有非常类似的功能,它们的差别仅在于:?QHash具有比QMap更快的查找速度。?QHash以 任意的顺序存储数据项,而QMap总是按照键Key的顺序存储数据。?QHash的键类型Key必须提供operator==()和一个 全局的qHash(Key)函数,而QMap的键类型Key必须提供operator<()函数。QMap和QHash的时间复杂度比较见 表2.4。容器类键查找插入平均最坏平均最坏QMapO(logn)O(logn)O(logn)O(logn )QHashAmort.O(1)O(n)Amort.O(1)O(n)1.QMap类QMap提供了一个从类型为Key的 键到类型为T的值的映射。通常,QMap存储的数据形式是一个键对应一个值,并且按照键Key的顺序存储数据。为了能够支持一键多值的情况 ,QMap提供了QMap::insertMulti()和QMap::values()函数。存储一键多值 的数据时,也可以使用QMultiMap容器,它继承自QMap。2.QHash类QHash具有与QMap 几乎完全相同的API。QHash维护着一张哈希表(HashTable),哈希表的大小与QHash的数据项的数目相适应。QHash 以任意的顺序组织它的数据。当存储数据的顺序无关紧要时,建议使用QHash作为存放数据的容器。QHash也可以存储一键多值形式的数据 ,它的子类QMultiHash实现了一键多值的语义。3.Java风格迭代器遍历容器对于每一个容器类,Qt都提供了两种 类型的Java风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。Java风格迭代器的两种分类见表2.5。容器类只读迭代 器类读写迭代器类QMap,QMultiMapQMapIteratorQMutableMa pIteratorQHash,QMultiHashQHashIterator>QMutableHashIterator【例】(简单)(CH204)在QMap中的插入、例(CH204).txt遍 历和修改例(CH204).txt。具体代码例(CH204).txt。其中,(a)for(;i.hasNext();)、i.nex t()、qDebug()<<""<的键和值时,调用的函数是不同的。在输出键的时候,调用QMapIterator::key();而在输出值的时候调用QMap Iterator::value(),为兼容不同编译器内部的算法,保证输出正确,在调用函数前必须先将迭代点移动到下一个位 置。(b)if(mi.findNext("111"))、mi.setValue("010"):首先查找某个<键,值>对,然后修改 值。Java风格的迭代器没有提供查找键的函数。因此,在本例中通过查找值的函数QMutableMapIterator::f indNext()来实现查找和修改。3.Java风格迭代器遍历容器最后编译、运行此程序,程序运行结果如下:"beijing" "111""nanjing" "025""shanghai" "021""beijing" "010""nanjing" "025""shanghai" "021"4.STL风格迭代器遍历容器对于每一个容器类,Qt都提供了两种类型的STL风格迭 代器数据类型:一种提供只读访问;另一种提供读写访问。STL风格迭代器的两种分类见表2.6。容器类只读迭代器类读写迭代器类QMa p,QMultiMapQMap::const_iteratorQMap:: iteratorQHash,QMultiHashQHash::const_iterato rQHash::iterator【例】(简单)(CH205)功能与使用Java风格迭代器的例子基本相同。不同的是,这 里通过查找键来例(CH205).txt实现值的修改。具体例(CH205).txt代码例(CH205).txt。其中,(a)mi. value()="010":将新的值直接赋给QMap::iterator::value()返回的 结果,因为该函数返回的是<键,值>对其中值的引用。第2章Qt5模板库、工具类及控件——QVariant类QVariant类【例 】(简单)(CH206)QVariant类的用法。新建QtWidgetsApplication(详见1.3.1节),项目名称为 “myVariant”,基类选择“QWidget”,类名保持“Widget”不变,取消选择“创建界面”复选框。建好项目后,在wid get.cpp文件例(CH206).txt中编写代码,具体例(CH206).txt内容例(CH206).txt。其中,(a)QV ariantv(709):声明一个QVariant变量v,并初始化为一个整数。此时,QVariant变量v包含了一个整数变量。( b)qDebug()<并输出。(c)QVariantw("Howareyou!"):声明一个QVariant变量w,并初始化为一个字符串。(d )qDebug()<转换为字符串并输出。(e)QMapmap:声明一个QMap变量map,使用字符串作为键,QV ariant变量作为值。(f)qDebug()< |
|
|
|
|
|
|
|