Qt对象模型和容器类

来源:互联网 发布:网络技术交流 编辑:程序博客网 时间:2024/05/17 09:00

Qt使用信号与槽的注意点:

1、需要继承自QObject或其子类;

2、在类声明的最开始添加Q_OBJECT宏;

3、槽中的参数类型要和信号的参数的类型相对应,且不能比信号的参数多;

4、信号只有声明,没有定义,且返回值为void类型;


Qt元对象系统(Meta-Object System)提供了对象间通信的信号和槽机制、运行时类型信息和动态属性系统。元对象系统基于以下3个条件:

1、该类必须继承自QObject类;

2、必须在类的私有声明区声明Q_OBJECT宏(在类定义时,如果没有指定public或private,则默认为private);

3、元对象编译器Meta-Object Compiler(moc),为QObject的子类实现元对象特性提供必要的代码;

元对象系统主要是为了实现信号和槽机制才被引入的,不过除了信号和槽机制以外,元对象系统还提供了其他一些特性:

1、QObject::metaObject()函数可以返回一个类的元对象,它是QMetaObject类的对象;

2、QMetaObject::className()可以再运行时以字符串形式返回类名,而不需要C++编辑器原生的运行时类型信息(RTTI)的支持;

3、QObject::inherits()函数返回一个对象是否是QObject继承树上一个类的实例的信息;

4、QObject::tr()和QObject::trUtf8()进行字符串翻译来实现国际化;

5、QObject::setProperty()和QObject::property()通过名字来动态设置或者获取对象属性;

6、MetaObject::newInstance()构造该类的一个新实例;


Qt容器类

Qt提供了一些顺序容器:QList、QLinkedList、QVector、QStack和QQueue。对于大多数应用程序而言,使用最多的,而且最好用的是QList,虽然它是作为一个数组列表,但是它在头部和尾部进行添加操作是很快速的。如果需要使用一个链表,那么可以使用QListedList;如果希望数据项可以占用连续的内存空间,可以使用QVector。而QStack和QQueue作为便捷类,分别提供了后进先出(LIFO)和先进先出(FIFO)语义。

Qt还提供了一些存储<键,值>得关联容器:QMap、QMultiMap、QHash、QMultiHash和QSet。

#include <QtCore/QCoreApplication>#include <QList>#include <QDebug>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QList<QString> list;    // 插入项目    list << "aa" << "bb" << "cc";    if(list[1] == "bb") list[1] = "ab";    // 将“cc”换为“bc”    list.replace(2,"bc");    // 输出整个列表,现在列表为aa ab bc    qDebug() << "the list is: ";    for(int i=0; i<list.size(); ++i){        qDebug() << list.at(i);    }    // 在列表尾部添加    list.append("dd");    // 在列表头部添加    list.prepend("mm");    // 从列表中删除第3个项目,并获取它    QString str = list.takeAt(2);    qDebug() << "at(2) item is: " << str;    // 现在列表为mm aa bc dd    qDebug() << "the list is: ";    for(int i=0; i<list.size(); ++i)    {        qDebug() << list.at(i);    }    // 在位置2插入项目    list.insert(2,"mm");    // 交换项目1和项目3    list.swap(1,3);    // 现在列表为mm bc mm aa dd    qDebug() << "the list is: ";    for(int i=0; i<list.size(); ++i)    {        qDebug() << list.at(i);    }    // 列表中是否包含“mm”    qDebug() << "contains 'mm' ?" << list.contains("mm");    //包含“mm”的个数    qDebug() << "the 'mm' count: " << list.count("mm");    // 第一个”mm“的位置,默认从位置0开始往前查找,返回第一个匹配的项目的位置    qDebug() << "the first 'mm' index: " <<list.indexOf("mm");    //第二个”mm“的位置,我们指定从位置1开始往前查找    qDebug() << "the second 'mm' index: " <<list.indexOf("mm",1);    return a.exec();}

Qt通用算法

在<QtAlgorithms>头文件中,Qt提供了一些全局的模板函数,这些函数在任何提供了STL风格的容器类上使用这些算法,包括QList、QLinkedList、QVector、QMap和QHash。在<QtGlobal>头文件中也提供了一些函数来实现一些经常使用的功能,比如qAbs()来获取绝对值、qBound()来获取数值边界、qMax()返回两个数中的最大值、qMin()返回两个数中的最小值、qRound()返回一个浮点数接近的整数值、还有前面与随机数有关的qrand()和qsrand()等。

#include <QtCore/QCoreApplication>#include <QTextCodec>#include <QVector>#include <QStringList>#include <QDebug>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QTextCodec::setCodecForTr(QTextCodec::codecForLocale());    QStringList list;    list << "one" << "two" << "three";    qDebug() << QObject::tr("qCopy()算法:");    QVector<QString> vect(3);    // 将list中所有项目复制到vect中    qCopy(list.begin(), list.end(), vect.begin());    // 结果为one,two,three    qDebug() << vect;    qDebug() << endl << QObject::tr("qEqual()算法:");    // 从list的开始到结束的所有项目与vect的开始及其后面的等数量的项目进行比较,全部相同则返回true    bool ret1 = qEqual(list.begin(), list.end(), vect.begin());    // 结果为true    qDebug() << "euqal: " << ret1;    qDebug() << endl << QObject::tr("qFind()算法:");    // 从list中查找"two",返回第一个对应的值的迭代器,如果没有找到则返回end()    QList<QString>::iterator i = qFind(list.begin(), list.end(), "two");    // 结果为"two"    qDebug() << *i;    qDebug() << endl << QObject::tr("qFill()算法:");    // 将list中的所有项目填充为"eleven"    qFill(list.begin(), list.end(), "eleven");    qDebug() << list; //结果eleven,eleven,eleven    QList<int> list1;    list1 << 3 << 3 << 6 << 6 << 6 << 8;    qDebug() << endl << QObject::tr("qCount()算法:");    int countOf6 = 0;    // 查找6的个数    qCount(list1.begin(), list1.end(), 6, countOf6);    // 结果为3    qDebug() << "countOf6: " << countOf6;    qDebug() << endl << QObject::tr("qLowerBound()算法:");    // 返回第一个出现5的位置,如果没有5,则返回5应该在的位置,list1被查找范围的项目必须是升序    QList<int>::iterator j = qLowerBound(list1.begin(), list1.end(), 5);    list1.insert(j, 5);    // 结果3,3,5,6,6,6,8    qDebug() << list1;    QList<int> list2;    list2 << 33 << 12 << 68 << 6 << 12;    qDebug() << endl << QObject::tr("qSort()算法:");    // 使用快速排序算法对list2进行升序排序,排序后两个12的位置不确定    qSort(list2.begin(), list2.end());    // 结果6,12,12,33,68    qDebug() << list2;    qDebug() << endl << QObject::tr("qStableSort()算法:");    // 使用一种稳定排序算法对list2进行升序排序,排序前在前面的12排序后依然在前面    qStableSort(list2.begin(), list2.end());    // 结果6,12,12,33,68    qDebug() << list2;    qDebug() << endl << QObject::tr("qGreater()算法:");    // 可以在qSort()算法中使其反向排序    qSort(list2.begin(), list2.end(), qGreater<int>());    // 结果68,33,12,12,6    qDebug() << list2;    qDebug() << endl << QObject::tr("qSwap()算法:");    double pi = 3.14;    double e = 2.71;    // 交换pi和e的值    qSwap(pi, e);    // 结果pi=2.71,e=3.14    qDebug() << "pi:" << pi << "e:" << e;    return a.exec();}
Qt隐式共享

隐式共享又称为写时复制。使用隐式共享类作为参数传递既安全又有效的,因为只有一个指向该数据的指针被传递了,只有当函数向它写入时才会复制该数据。

    QPixmap p1, p2;    p1.load("image.bmp");    p2 = p1;            //p1与p2共享数据    QPainter paint;    paint.begin(&p2);   //p2被修改    paint.drawText(0, 50, "Hi");    paint.end();
一个共享类由一个指向一个共享数据块的指针和数据组成,在共享数据块中包含了一个引用计数。当一个共享对象被建立时,会设置引用计数为1。例如这里QPixmap类是一个隐式共享类,开始时p1和p2的引用计数都为1。每当有新的对象引用了共享数据时引用计数都会递增,而当有对象不再引用这个共享数据时引用计数就会递减,当引用计数为0时,这个共享数据就会被销毁掉。例如这个执行了"p2 = p1;"语句后,p2便于p1共享同一个数据,这时p1的引用计数为2,而p2的引用计数为0,所以p2以前指向的数据结构将会被销毁掉。当处理共享对象时,有两种复制对象的方法:深拷贝和浅拷贝。深拷贝意味着复制一个对象,而浅拷贝则是复制一个引用(仅仅是一个指向共享数据块的指针)。一个深拷贝是非常昂贵的,需要消耗很多的内存和CPU资源;而浅拷贝则非常快速,因为它只需要设置一个指针和增加引用计数的值。当隐式共享类使用"="操作符时就是使用浅拷贝,如上面的"p2 = p1;"语句。但是当一个对象被修改时,就必须进行一次深拷贝,比如上面程序中"paint.begin(&p2);"语句要对p2进行修改,这是就要对数据进行深拷贝,使p2和p1指向不同的数据结构,然后将p1的引用计数设为1,p2的引用计数也设为1。

共享的好处是程序不需要进行不必要的数据复制,可以减少数据的拷贝和使用更少内存,对象也可以很容易地被分配,或者作为参数被传递,或者从函数被返回。隐式共享在后台进行,在实际编程中不必去关注它。Qt中主要的隐式共享类有:QByteArray、QCursor、QFont、QPixmap、QString、QUrl、QVariant和所有的容器类等(帮助文档查看Implicit Sharing关键字)。


Qt正则表达式

正则表达式就是在一个文本中匹配字符串的一种模式,可以简写为"regexp"。应用如下:

1、验证。一个regexp可以测试一个子字符串是否符合一些标准。例如,是一个整数或者不包含任何空格等;

2、搜索。一个regexp提供了比简单的子字符串匹配更强大的模式匹配。例如,匹配单词mail或者letter,而不匹配单词email或者letterbox。

3、查找和替换。一个regexp可以使用一个不同的字符串替换一个字符串中所有要替换的子字符串。例如,使用Mail来替换一个字符串中所有的M字符,但是如果M字符后面有ail 时不进行替换;

4、字符串分割。一个regexp可以识别在哪里进行字符串分割。例如,分割制表符隔离的字符串;

Qt中的QRegExp类实现了使用正则表达式进行模式匹配。QRegExp是以Perl的正则表达式语言为蓝本的,它完全支持Unicode。QRegExp中的语法规则可以使用setPatternSyntax()函数来更改。

#include "widget.h"#include "ui_widget.h"#include <QDebug>Widget::Widget(QWidget *parent) :    QWidget(parent),    ui(new Ui::Widget){    ui->setupUi(this);    QRegExp rx("^\\d\\d?$");       // 两个字符都必须为数字,第二个字符可以没有    qDebug() << rx.indexIn("a1");  // 结果为-1,不是数字开头    qDebug() << rx.indexIn("5");   // 结果为0    qDebug() << rx.indexIn("5b");  // 结果为-1,第二个字符不是数字    qDebug() << rx.indexIn("12");  // 结果为0    qDebug() << rx.indexIn("123"); // 结果为-1,超过了两个字符    qDebug() << "*******************";         // 输出分割符,为了显示清晰    rx.setPattern("\\b(mail|letter)\\b");      // 匹配mail或者letter单词    qDebug() << rx.indexIn("emailletter");     // 结果为-1,mail不是一个单词    qDebug() << rx.indexIn("my mail");         // 返回3    qDebug() << rx.indexIn("my email letter"); //返回9    qDebug() << "*******************";    rx.setPattern("M(?!ail)");     // 匹配字符M,其后面不能跟有ail字符    QString str1 = "this is M";    str1.replace(rx,"Mail");       // 使用"Mail"替换匹配到的字符    qDebug() << "str1: " << str1;  // 结果为this is Mail    QString str2 = "my M,your Ms,his Mail";    str2.replace(rx,"Mail");    qDebug() << "str2: " << str2;  // 结果为my Mail,your Mails,his Mail    qDebug() << "*******************";    QString str3 = "One Eric another Eirik, and an Ericsson. "                   "How many Eiriks, Eric?"; // 一个字符串如果一行写不完,换行后两行都需要加双引号    QRegExp rx2("\\bEi?ri[ck]\\b");          // 匹配Eric或者Eirik    int pos = 0;    int count = 0;    while (pos >= 0) {        pos = rx2.indexIn(str3, pos);        if (pos >= 0) {            ++pos;      // 从匹配的字符的下一个字符开始匹配            ++count;    // 匹配到的数目加1        }    }    qDebug() << "count: " << count;                  // 结果为3    QRegExp rx3("*.txt");    rx3.setPatternSyntax(QRegExp::Wildcard);    qDebug() << rx3.exactMatch("README.txt");        // 结果为true    qDebug() << rx3.exactMatch("welcome.txt.bak");   // 结果为false    QRegExp rx4("(\\d+)");    QString str4 = "Offsets: 12 14 99 231 7";    QStringList list;    int pos2 = 0;    while ((pos2 = rx4.indexIn(str4, pos2)) != -1) {        list << rx4.cap(1);          // 第一个捕获到的文本        pos2 += rx4.matchedLength(); // 上一个匹配的字符串的长度    }    qDebug() << list;                // 结果12,14,99,231,7    QRegExp rxlen("(\\d+)(?:\\s*)(cm|inch)");    int pos3 = rxlen.indexIn("Length: 189cm");    if (pos3 > -1) {        QString value = rxlen.cap(1);  // 结果为189        QString unit = rxlen.cap(2);   // 结果为cm        QString string = rxlen.cap(0); // 结果为189cm        qDebug() << value << unit << string;    }    QRegExp rx5("\\b(\\w+)\\W+\\1\\b");    rx5.setCaseSensitivity(Qt::CaseInsensitive);  // 设置不区分大小写    qDebug() << rx5.indexIn("Hello--hello");      // 结果为0    qDebug() << rx5.cap(1);                       // 结果为Hello    QRegExp rx6("\\b你好\\b");       // 匹配中文    qDebug() << rx6.indexIn("你好"); // 结果为0    qDebug() << rx6.cap(0);         // 整个字符串完全匹配,使用cap(0)捕获,结果为“你好”}Widget::~Widget(){    delete ui;}
这里使用了QRegExp的indexIng()函数,它从指定的位置开始向后对字符串进行匹配,默认是从字符串开始进行匹配。如果匹配成功,返回第一个匹配到的位置的索引,如果没有匹配到则返回-1。setPattern()函数用来输入一个regexp。而QString的replace()函数可以使用给定的regexp和替换字符来进行字符串的替换。


0 0