zoukankan      html  css  js  c++  java
  • 我的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() 用指定的字符串代替原字符串中的某些字符

      为了解决特定场景比如去除一个字符串两端的空白(空白字符包括回车字符“ ”,换行字符“ ”,制表符“ ”和空格字符等),QString提供了特定的函数。

    函数名称 函数功能
    trimmed()        移出字符串两端的空白字符
    simplified() 移除字符串两端的空白字符,使用单个空格字符“ ”代替字符串中出现的空白字符
    1 QString str="  Hello 	  QT 
     ! ";
    2 str=str.trimmed();
    3 
    4 //str=" Hello 	 to 
     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),也可以存储传统的以“”结尾的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 }

      好了,关于字符串类和容器类先说这么多,如有错误,欢迎指正。

  • 相关阅读:
    Why Choose Jetty?
    Jetty 的工作原理以及与 Tomcat 的比较
    Tomcat设计模式
    Servlet 工作原理解析
    Tomcat 系统架构
    spring boot 打包方式 spring boot 整合mybaits REST services
    wireshark udp 序列号 User Datagram Protocol UDP
    Maven 的聚合(多模块)和 Parent 继承
    缓存策略 半自动化就是mybaitis只支持数据库查出的数据映射到pojo类上,而实体到数据库的映射需要自己编写sql语句实现,相较于hibernate这种完全自动化的框架我更喜欢mybatis
    Mybatis解决sql中like通配符模糊匹配 构造方法覆盖 mybits 增删改
  • 原文地址:https://www.cnblogs.com/jingliming/p/4965199.html
Copyright © 2011-2022 走看看