Qt学习笔记(5) — Qt 类库【C++】

目录

    • 一、Qt核心特点
      • 1. 元对象系统
      • 2. 信号与槽的关联方式
    • 二、Qt全局定义(常用头文件)
      • 1. 头文件
        • 1)数据类型定义
        • 2)函数
        • 3)宏定义
    • 三、容器类
      • 1. 顺序容器
        • 1)QList
        • 2)QLinkedList
        • 3)QVector
        • 4)QStack
        • 5)QQueue
      • 2. 关联容器
        • 1)QSet
        • 2)QMap
        • 3)QMUltiMap
        • 4)QHash
        • 5)QMultiHash
    • 四、容器的迭代
      • 1. Java型迭代器
        • 1)顺序容器类的迭代器使用
        • 2)关联容器的迭代器使用
      • 2. STL型迭代器
        • 1)顺序容器的迭代器使用
        • 2)关联容器的迭代器使用
      • 3. foreach关键字


一、Qt核心特点

Qt core是Qt类库的核心,所有其他模块都依赖于此模块,若使用qmake来构建项目,Qt core类里的模块则是被自动加入的;
Qt为C++语言增加的特性,就是在Qt core的模块里实现的吗,这些扩展特性由Qt的元对象系统实现,包括信号与槽机制,属性系统、动态类型转化等。

1. 元对象系统

Qt的元对象系统 提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统:

元对象由一下三个基础组成:

  • QObject类是所有使用元对象系统的类的基类
  • 在一个类的private声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽
  • MOC(元对象编译器)为每个QOject的子类提供必要的代码来实现元对象系统的特性

构建项目时,MOC工具读取C++源文件,发现类中有Q_OBJECT宏时,会为这个类生成另外一个包含有元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件被一起被编译和链接

除了信号与槽机制外,元对象还提供了一些功能:

  • QObject::metaObject()函数:返回类关联的元对象,元对象类QMetaObject包含了访问元对象的一些接口函数,比如 QMetaObject::className() 函数可在运行时返回类的名称字符串:
QObject *obj = new QPushButton;
obj->metaObject()->className();//返回“QPushButton”
  • QMetaObject::newInstance()函数: 创建类的一个实例
  • QObject::inherits(const char* className)函数:判断一个对象实例是否是名称为className的类或QObject的实例
QTimer * timer = new QTimer;
timer->inherits("QTimer");//true
timer->inherits("QObject");//true;
timer->inherits("QAbstructButton");//false 
  • QObject::tr() 和 QObject::trUtf8()函数:可翻译字符串,用于多语言界面设计
  • QObject::setProperty()和QObject::property()函数:用于通过属性名称动态 设置和获取属性值
  • QObject及其子类,还可以使用 qobject_cast() 函数进行动态投射(类似于C++多态调用那样),他并不区分Qt内建类型和用户自定义类型

2. 信号与槽的关联方式

不管是那种类型的connect函数,最后都有一个参数Qt::connectionType type(是个枚举类型,缺省是 Qt::AutoConnection),有以下几种取值:

  • Qt::AutoConnection:若信号的接受者和发送者在同一个线程,就使用Qt::DirectConnection方式;否则使用Qt::queuedConnection方式,在信号发射时自动确定关联方式
  • Qt::DirectConnection:信号被发射时槽函数立即执行,槽函数和信号在同一个线程
  • Qt::queuedConnection:在事件循环回到接收者线程后执行槽函数,槽函数与信号不在一个线程
  • Qt::BlockingQueuedConnection:与Qt::queuedConnection类似,只是信号线程会阻塞知道槽函数执行完毕;当信号与槽函数在同一个线程时绝对不能使用此方式,否则会造成死锁

自定义信号及其使用

信号就是在类定义里声明的一个函数,但这个函数无需实现,只需发射(emit)

class QPerson : public QObject
{
	Q_OBJECT
private:
	int m_age=10;
public:
	void incAge();
signals:
	void ageChanged(int value);
};

信号函数必须是无返回值的函数,但是可以有参数,无需实现,只要在某些条件下发射信号

void QPerson::incAge()
{
	m_age++;
	emit ageChanged(m_age);
}

二、Qt全局定义(常用头文件)

1. 头文件

头文件中包含了Qt类库的一些全局定义,包括基本数据类型、函数和宏,一般的Qt类的头文件都会包含该文件,so可以不用显式的包含次头文件

1)数据类型定义

为了确保在各个平台上各数据类型都有统一确定的长度,Qt为各种常见的数据类型定义了类型符号,如qint8就是signed char的类型定义;

中定义的数据类型如下:

Qt学习笔记(5) — Qt 类库【C++】_第1张图片
qfloat16是Qt 5.9.0中新增的一个类,用于表示16位的浮点数,要使用的话,需包含头文件

2)函数

包含的一些常用函数的定义,大部分是以模板类型作为参数,返回相应的模板类型,模板类型可以用其他类型替换;

一般要是以double或float为参数的,几乎都有两个参数版本的同名函数

一些常用的全局函数如下所示:
Qt学习笔记(5) — Qt 类库【C++】_第2张图片
还有一些基础的数学运算函数在头文件中定义,如三角运算函数、弧度与角度间的黄钻换函数等

3)宏定义

中有很多宏定义,常用的宏定义有如下这些:

  • QT_VERSION
    表示Qt编译期版本,比如Qt编译期版本为5.9.0,则QT_VERSION为0x050901;
    常用于条件编译设置,根据Qt版本不同,编译不同的代码段

  • QT_VERSION_CHECK
    是Qt版本号的一个整数表示

  • QT_VERSION_STR
    是Qt版本号的字符串

  • Q_BYTE_ORDER、Q_BIG_ENDIAN、Q_LITTLE_ENDIAN*:
    Q_BYTE_ORDER表示系统内存中数据的字节序,Q_BIG_ENDIAN表示大端字节序,Q_LITTLE_ENDIAN表示小端字节序;
    一般用于判断系统字节序

  • Q_DECL_IMPORT、Q_DECL_EXPORT
    用于导入或导出库的内容,常用与使用或设计共享库

  • Q_DECL_OVERRIDE:用于在类定义中重载一个虚函数,若重载的虚函数没有进行任何重载操作,编译器将会报错

  • Q_DECL_FINAL
    这个宏讲一个虚函数定义为最终级别,不能再被继承,或已定义一个类不能再被继承

  • Q_UNUSED(name)
    该宏用于在函数中定义不在函数体里使用的参数,在函数里要是某变量没有被使用,也没有Q_UNUSED定义,编译期会出现参数未使用的警告

  • foreach(variable,container):
    用于容器类的遍历

  • forever
    用于构造一个无限循环

  • qDebug(const char * message)
    在debugger窗体显式信息,如编译期设置了Qt_NO_BEBUG_OUTPUT,则不做任何输出

三、容器类

Qt提供了多个基于模板的容器类,他们可以用于存储指定类型的数据项;

Qt的容器类比stl的容器类更轻巧、安全和易于使用,他们是隐式共享和可重入的,且他们进行了速度和存储优化,因此可以减少可执行文件的大小;

他们是线程安全的,可以在作为只读容器时被多个线程同时访问;

容器类是基于模板的类,如QList,T是一个具体的类型,可以是int、float等简单类型,也可以是QString、QDate等类,但不能是QObject或其任何子类,T必须是一个可赋值的类型,即T必须提供一个缺省的构造函数,一个可拷贝构造函数和一个赋值运算符

Qt容器类分为顺序容器和关联容器;Qt还提供了foreach宏用于遍历容器内的所有数据项

1. 顺序容器

1)QList

QList是最常用的容器类,以数组列表形式实现,在其前面和后面增添数据很快,以下标方式访问数据项,也提供at()函数;

成员函数有:insert()、replace()、removeAt()、move()、swap()、append()、prepend()、removeFirst()和 removeLast()等;

2)QLinkedList

他是链式列表,数据项不是用连续的内存存储的,它基于迭代器访问数据项,并且插入和删除数据项的操作时间相同;

他不提供基于下标的访问方式,其余接口与QList基本相同

3)QVector

提供动态数组功能,以下标索引访问数据;

函数接口几乎与QList基本相同,QVector 比 QList 的性能更好,因为它的数据项是连续存储的

4)QStack

类似于堆栈的具有先进后出特性的容器类,push()和pop()是主要的接口函数;

#include 
#include 
void TestQStack()
{
    QStack<int> stack;
    stack.push(10);
    stack.push(20);
    stack.push(30);
    while (!stack.isEmpty())
       qDebug()<< stack.pop();//30 20 10
}
5)QQueue

类似于队列的具有先进先出特性的容器类,enqueue()和dequeue()是主要的操作函数;

#include 
#include 
void TestQQueue()
{
    QQueue<int> queue;
    queue.enqueue(10);
    queue.enqueue(20);
    queue.enqueue(30);
    while (!queue.isEmpty())
        qDebug()<< queue.dequeue();//10 20 30
}

2. 关联容器

1)QSet

是基于散列表的集合模板类,存储数据的顺序是不定的,查找值的速度非常快;

QSet内部就是QHash实现的;

#include 
#include 
void TestQQueue()
{
    QSet<QString> set;
    set<<"dog"<<"cat"<<"tiger";
    if(!set.contains("cat"))
    {
    	qDebug<<"容器内不包含cat";
    }
}
2)QMap

QMap提供一个字典(关联数组),一个键映射到一个值;

QMap存储数据时按照键的顺序,若不在乎存储顺序,建议使用QHash更快;

#include 
#include 
void TestQMap()
{
    //QMap存储数据是按照键的顺序
    QMap<int,char> _map;
    for(int i=97;i<123;i++)
    {
        _map[i]=i;
    }
    foreach(char c,_map)
    {
         qDebug()<<c;//按顺序a b c...输出
    }
    qDebug()<<_map[100];//d
    char upper_a=_map.value(65,'A');
    qDebug()<<upper_a;//A
}

若在映射表中没有找到指定的键,会返回一个缺省构造值,如,若值得类型是字符串,会返回一个空字符串;

在使用value()查找键值时,还可以指定一个缺省的返回值:

timeout=map.value("TIMEOUT",30);

表示如果在map里找到键“TIMEOUT”了就返回关联的值,未找到就返回30

3)QMUltiMap

他是QMap的子类,用于处理多值映射的便利类;

QMultiMap不提供[]操作符,使用value()函数访问最新插入的键的单个值,若要获取一个键对应的值所有值,使用values()函数,返回值是QList类型;

#include 
#include 
void TestQMultiMap()
{
    QMultiMap<char,QString> _multiMap;
    //QMultiMap 不提供“[]”操作符
    //_multiMap['0']="ss";
    _multiMap.insert('*',"pointer");
    _multiMap.insert('*',"all");
    _multiMap.insert('*',"multiplication sign");
    qDebug()<<_multiMap.value('*');//最新的值 multiplication sign
    QList<QString> _list=_multiMap.values('*');
    foreach(QString str,_list)
    {
        qDebug()<<str;//"multiplication sign"
                      //"all"
                      //"pointer"
    }
    for(int i=0;i<_list.size();i++)
    {
        qDebug()<<_list[i];//"multiplication sign"
                           //"all"
                           //"pointer"
    }
}
4)QHash

他是基于散列表来实现字典功能的模板类,具有非常快的查找速度;

QHash的数据项是无序的,QHash必须提供“==”运算符和一个QHash()的全局散列函数;

5)QMultiHash

QMultiHash 是 QHash 的子类,是用于处理多值映射的便利类,其用法与 QMultiMap 类似

四、容器的迭代

Qt有两种类型的迭代器:Java型迭代器 和 STL型迭代器

1. Java型迭代器

Qt提供有以下几种迭代器,对于每个容器类,有两个Java型迭代器,一个只读迭代器,一个读写迭代器:

容器类 只读迭代器 读写迭代器
QList, QQueue QListIterator QMutableListIterator
QLinkedList QLinkedListIterator QMutableLinkedList
QVector, QStack QVectorIterator QVectorMutableIterator
QSet QSetIterator QSetMutableIterator
QMap, QMultiMap QMapiterator QMutaleMapIterator
QHash, QMultiHash QHashIterator QMutableHashIterator
1)顺序容器类的迭代器使用

Java类型的迭代器的指针不是指向一个数据项,而是指向数据项之间的位置:
Qt学习笔记(5) — Qt 类库【C++】_第3张图片

QListIterator常用函数

  • void toFront():迭代器移动到列表的最前面(第一个数据项之前)
  • void toBack():迭代器移动到列表的最后面(最后一个数据项之后)
  • bool hasNext():若迭代器不位于最后位置,返回true
  • bool hasPrevious():若迭代器不位于最前面的位置,返回true
  • const T & next():返回下一个数据项,并且迭代器后移一个位置
  • const T & peekNext():返回下一个数据项,但迭代器不移动
  • const T & previous():返回前一个数据项,并且迭代器前移一个位置
  • const T & peekPrevious():返回前一个数据项,但迭代器不移动

QMutableListIterator是读写迭代器,可用于修改容器中的数据;

remove()函数移除next()函数刚刚跳过去的数据项,不会使迭代器失效;

setValue()函数可以修改刚刚跳过去的数据项的值;

#include 
void Test()
{
	QList<QString> list;
	list<<"A"<<"B"<<"C"<<"D";
	QListIterator<QString> i(list);
	while(i.hasNext())
	{
		qDebug<<i.next();
	}	
	//反向遍历
	i.toBack();
	while(i.hasPrevious())
	{
		qDebug()<<i.previous();
	}	

	//读写迭代器
	QList<int> list2;
	list2<<1<<2<<3<<4<<5;
	QMutableListIterator<int> it(list2);
	while(it.hasNext())
	{
		if(it.next() % 2==0)
		{
			it.remove();
		}
	}
}
2)关联容器的迭代器使用

对于关联容器的迭代器,普通函数接口同顺序性容器的哪些基本类似,主要是增加了key()和value()函数用于获取刚刚跳过的数据项的键和值;

void Test()
{
	QMap<QString,QString> map;
	map.insert("Paris","France");
	map.insert("New York","USA");
	map.insert("Mexico City","USA");
	//...
	QMutableMapIterator<QString,QString> i(map);
	while(i.hasNext())
	{
		if(i.next().endsWith("City"))
		{
			i.remove();
		}
	}
}

若是在多值容器里遍历,可用findNext()或 findPrevious()查找:

QMutableMapIterator<QString,QString> i(map);
while(i.findNext("USA"))
{
	i.remove();		
}

2. STL型迭代器

STL迭代器与Qt和STL的原生算法兼容,并且进行了速度优化,,因此它的效率更高;

类似的,对于每个容器类,也有两个STL型迭代器:比如对于QList, 则有只读迭代器QList::const_iterator 和 读写迭代器QList::iterator;

STL型迭代器是数组的指针,所以“++”运算符使迭代器指向下一个数据项,“*”运算符返回数据内容:
Qt学习笔记(5) — Qt 类库【C++】_第4张图片
end()函数指向一个虚拟的表示结尾的数据项,实际是无效的,一般作为循环结束条件;

1)顺序容器的迭代器使用

constBegin()和constEnd()是用于只读迭代器的,表示起始位置和结束位置;

QList<QString> list;
list<<"A"<<"B"<<"C"<<"D";
QList<Qstring>::const_iterator i;
for(i=list.constBegin();i!=list.constEnd();++i)
{
	qDebug<< *i;
}
//使用反向读写迭代器
QList<QString>::reverse_iterator i2;
for(i2=list.rbegin();i2!=list.rend();++i2)
{
	*i=i->toLower();
}
2)关联容器的迭代器使用

对于关联容器类QMap 和 QHash,迭代器的“*”操作符返回数据项的值,若想返回键,则用key()函数,对应的value()函数返回值:

QMap<int,int> map;
//....
QMap<int,int>::const_iterator i;
for(i=map.constBegin();i!=map.constEnd();++i)
{
	qDebug()<<i.key()<<":"<<i.values();
}

3. foreach关键字

如只是需要遍历容器,则可以使用foreach关键字;

foreach的语法是:foreach (variable,container)

QLinkedList<QString> list;
...
QString str;
foreach(str,list)
{
	qDebug()<<str;
}

//第二种方式
foreach(const QString &str,liat)
{
	qDebug()<<str;
}

你可能感兴趣的:(笔记,qt,C++,qt,c++)