第11章 容器类
Qt提供了自己的容器类,在编写Qt程序时,既可以使用Qt容器,也可以使用STL容器。
- 连续容器
1)向量QVector<T>
:它是一种与数组相似的数据结构,它可以把项存储到内存中相邻的位置。向量与普通C++数组的区别在于:向量知道自己的大小并且可以被重新定义大小。
在向量的末尾添加或删除额外的项是非常快速有效的,而在向量的前面或中间插入或删除项则是比较耗时的,因为这样可能导致大量的项目在内存中移动一个位置。对于较大的向量来说,这种现象尤为严重,因此Qt提供了QLinkList<T>容器。
2)链表QLinkList<T>
: 它是一种把项存储到内存中不相邻位置的数据结构。与向量不同,链表不支持快速的随机访问,但它提供“常量时间”的插入和删除。链表并未提供[]操作符,因此必须使用迭代器来遍历项而不能使用整数索引。当向一个很大的列表中间插入项目时,QLinklist比下面的QList拥有更好的性能。
3)列表QList<T>
: 它结合了单一类中QVector<T>和QLinkList<T>的最重要的优点,是目前最常用的容器类。它存储了给定值的一个列表,而且这些值可以通过索引访问。在内部QList使用数组实现,它支持随机访问。在QList<T>的任意一端插入或者移除项都是非常快速的。除非我们想在一个极大的列表中执行插入或者要求列表中的元素都必须占据连续的内存地址,否则QList<T>通常是最适合采用的多用途容器类。
这三个连续容器类还衍生出了一些方便的子类:QStringList容器常用于字符串处理,他是QList<Qstring>的子类。QStack<T>是一个可以提供push()、pop()、和top()方法的 向量。QQueue<T>是一个可以提供enqueue()、dequeue()和head()的 列表。
对于前面所讲过的容器类,值类型T可以是一个int,double类型,指针类型,或满足一定条件的类(不能是派生自QObject的Qt类,因为他们没有复制构造函数和赋值操作符,如果想要使用派生自QObject的类,可以存储指向QObject类的指针)。
值类型T也可以是一个容器。
4)Qt提供两类迭代器用于遍历存储在容器中的项:Java风格的迭代器和STL风格的迭代器。Java风格迭代器易于使用,STL风格迭代器可以结合STL的一般算法而具有更强大的功能。
Java风格迭代器可分为:只读迭代器和读-写迭代器。对每一个连续容器C,只读迭代器记为:“C+Iterator<T>",而读-写迭代器则在其名字中含有“Multable”字样。例如:QVectorIterator<T>,QMultableVectorIterator<T>。读-写迭代器在遍历时提供了插入、修改以及删除项的函数。
当使用Java风格迭代器时,必须首先牢记的是:它们本身并不是直接指向项的,而是定位在第一项之前,最后一项之后或者是两项之间。
用法:
QListIterator<double> i (list); //只读迭代器定义并初始化
i.hasnext(); //如果迭代器右边有 一个项,则hasnext()函数返回true
i.next(); //next()函数返回迭代器右边的项并且将迭代器向后移动一个位置,类似的还有previous()函数
i.toBack(); //将迭代器定位到最后一项之后的位置
QMultableListIterator<double> i (list); //读-写迭代器定义并初始化
i.remove(); //remove()函数删除迭代器右边的项并且将迭代器往后移一个位置
STL风格迭代器也有两种类型: 对每一个连续容器C<T>,STL风格迭代器类型有C<T>::iterator和C<T>::const_iterator。这两者的区别在于const_iterator不允许修改数据。
STL风格的迭代器的语法是模仿C++数组的指针。我们可以使用++ 和-- 操作符来移动下一项或前一项,而使用一元操作符*来获得当前项。例如:对于QVector<T>,iterator和const_iterator类型只是T*和const T*的别名。
用法:
QList<double>::iterator i = list.begin(); //定义并初始化STL风格迭代器 i
//容器的begin()函数返回引用容器中第一项的STL风格迭代器(例如:list[0]),而end()函数返回引用“最后一个项之后的”项的迭代器(例如:对于一个大小为5的列表,取list[5])。
i++; // 迭代器右移1个单元
Qt还提供而来一种在连续容器中遍历的方式 – foreach循环。例如:
QLinkList<Movie> list;
foreach(Movie movie, list) {
if(movie.title() == "Citizen Kane") {
std::cout << "Found Citizen Kane" << std::endl;
break;
}
foreach伪关键字按照标准的for循环实现。在循环的每一次迭代中,迭代变量(如上例中的movie)都被设为一个新项,从容器的第一项开始向后迭代。foreach循环会在进入循环时自动复制一个容器,因此即使在迭代过程中修改了容器类,也不会影响到循环。
- 关联容器
关联容器可以保存任意多个具有相同类型的项,且它们由一个键索引。Qt提供两个主要的关联容器类:QMap<K,T>和QHash<K,T>。
1)映射QMap<K, T>是一个以升序键顺序存储键值对的数据结构。它提供了一个字典(关联数组),将Key类型的键值映射到T类型的值上。QMap按照键值升序排列,这种排列使它可以提供良好的查找和插入性能,以及键序的迭代。如果不关心存储顺序,那么可以使用下面的QHash,因为QHash更快。
QMap<K, T>的K和T数据类型可以是int,double,指针类型,或满足一定条件的类。用法:
QMap<QString,int> map;
map.insert("eins",1);
map.insert("sieben",7);
map.insert("dreiundzwaning",23); //在映射中插入项
int val = map.value("dreiundzwaning"); //获得键值
映射通常都是单一值,如果赋予一个现有的键一个新值,则原有的旧值将被该新值取代,以确保两个项不会共有同一个键。通过使用insertMulti()函数或QMultiMpa<K,T>子类,可以让多个值有相同的键。
QMultiMap<int,QString> multiMap;
multiMap.insert(1,“one");
multiMap.insert(1,"eins");
multiMap.insert(1,"uno");
QList<QString> vals = multiMap.values(1); //返回一个给定键对应的所有值的QList列表
2)QHash<K,T>是一个在哈希表中存储键值对的数据结构。它提供了比QMap<K,T>更快的查找功能。QHash的数据是按照任意顺序存储的。
QHash<K,T>为它内部的哈希表自动分配最初的存储区域,并在有项被插入或删除时重新划分所分配的存储区域的大小。哈希表通常都是单一值,可以通过使用insertMulti()函数或MultiHash<K,T>子类,可以将多个值赋给同一个键。
- 通用算法
<QtAlgorithms>的头文件声明了在容器类上实现基本算法的一套全局模板函数。这些函数大部分都是在STL风格的迭代器上工作。
1)qFind()算法:在容器类中线性查找一个特定的值。它接受一个“begin”和“end”迭代器,并且返回一个与其匹配的指向第一项的迭代器。如果没有匹配的项,则返回“end”。用法:
QStringList::iterator i = qFind(list.begin(),list.end(),“Karl”);
2)qBinaryFind()算法:适用于容器中的项都是顺序存储的情况,且使用二分查找,效率更高。
3)qFill()算法:用一个特定的值组装一个容器。用法:
QLinkList<int> list(10);
qFill(list.begin(),list.end(),1000); //将list的每一项都赋值为1000
4)qCopy()算法:将一个容器类的值复制到另一个容器类中。用法:
QVector<int> vect(list.count());
qCopy(list.begin(),list.end(),vect.begin()); //将list.begin()到list.end()之间的值复制到vect.begin()开始往后的项中
qCopy()也可以用来在同一个容器类中复制值,只要数据来源范围与目标范围不重叠。如:
qCony(list.begin(),list.begin()+2,list.end()-2); 它将列表的前两项复制到列表的后两项。
5)qSort()算法: 以升序排列容器中类中的项。默认情况下qSort()使用“<”操作符对项进行比较。用法:
qSort(list.begin(),list.end());
我们也可以将qGreator()作为第三个参数来传递,来使qSort()为升序排列。如:
qSort(list.begin(),list.end(),qGreator<int>());
6)qStableSort()算法:功能与qSort()相似,但qStableSort()算法可以保证进行对等比较的项在排序之后表现出与之前相同的顺序。
7)qDeleteAll()算法:对每一个存储在容器类中的指针调用delete。这仅对那些指针类型的容器才有意义。在调用之后,这些项仍然作为悬摆指针存在于容器类之上,因此通常我们也应该在容器类上调用clear()。用法:
qDeleteAll(list);
list.clear();
8)qSwap()算法:交换两个变量的值。用法:
int x1,x2;qSwap(x1,x2);
- 字符串、字节数组和变量
1)字符串类QString:C++提供两种字符串,传统C语言型的以“\0”结尾的字符数组和std::string类。与这两种字符串不同,QString支持16位Unicode编码。从概念上说,可以将QString看成是QChar向量。
length()函数:该函数会返回字符串的大小。要注意的是,QString可以嵌入“\0”字符,length()函数会返回包括被嵌入的“\0”字符的整个字符串的大小。
+&+=操作符:QString为连接两个字符串提供了一个二进制+操作符,还为在字符串后追加字符串提供了+=操作符。用法:
QString str = "User: ";
str += userName + "\n";
QString::append()函数与+=操作符的功能相同:str = "User: "; str.append(userName); str.append("\n");
arg()函数:用来替换前面的“%n”参数。如:
str = QString(“%1 %2 (%3s-%4s)”)
.arg(“permission”).arg(“society”).arg(1950).arg(1970);
在这个例子中,“permission”会取代“%1”,“ society”会去代“%2”,“1950”与“1970”会依次取代“%3”和“%4”相应的结果为“permission society (1950s-1970s)”。
将数字转换为字符串:使用QString::number()静态函数,或者也可以使用setNum()函数。用法:
str = QString::number(59.6); 或 str.setNum(59.6);
将字符串转换为数字:使用toInt()、toLongLong()、toDouble()等函数。这些函数接受一个指向bool变量的指针,并且根据转换的结果将变量值设为true或false。如果转换没有完成,这些函数将返回0。用法:
bool ok; double d = str.toDouble(&ok);
mid()函数:该函数返回在给定位置(第一个参数)开始且到达给定长度(第二个参数)的子串。如果省略第二个参数,该函数将返回在给定位置开始到字符串末端结束的子串。用法:
QString str = "polluter pays principle"; qDebug() << str.mid(9);
也可以使用left()函数和right()函数来完成相似的任务。它俩都可以接收字符的数量n,并且返回字符串的前n个字符或最后n个字符。用法:
qDebug() << str.left(8) << str.right(9);
indexOf()函数:查明一个字符串是否包含一个特定的字符、子串或者正则表达式。查找成功时返回字符(或子串首字符)在字符串中的位置,失败时该函数返回-1。用法:
QString str = "the middle bit";
int i = str.indexOf("middle"); //该函数将i的值设为4
利用==操作符进行字符串的比较是区分大小写的。如果比较并不区分大小写,则可以使用toUpper() 或toLower()函数。用法:
if(fileName.toLower() == "readme.txt");
如果想用一个字符串来代替另一个字符串中的某一部分,可以使用replace(); 用法:
str.replace(2,6,"Sunny");
replace()函数也可以用来让第二个参数代替所有第一个参数出现的地方。如:
str.replace("&","&"); //使用“&”代替字符串中所有的“&”
使用QString::split()函数可以把一个字符串分成一些QStringList子串。用法:
QString str = “polluter pays principle”;
QString words = str.split(" ");//利用空格(“ ”)把字符串“polluter pays principle”分成了三个子串“polluter”、“pays”和“principle”
使用join()函数,QStringList中的项可以连接起来形成一个单一的字符串。 在每一对被连接的字符串之间都要插入join()的参数。如:str = words.join("\n"); 创建一个QStringList类对象words包含的所有字符串组成的由换行符分隔的简单字符串。
2)字节数组类QByteArray提供了一个字节数组,他可以用来存储原始字节(包括‘\0’)和传统的以‘\0’结尾的8位字符串。
QByteArray具有与QString相似的应用编程接口,但QByteArry对于存储原始的二进制数据以及8位编码的文本字符串非常有用。(一般来说,我们推荐使用QString而不是QByteArray来存储文本,因为QString支持Unicode编码)
QByteArray自动保证“最后一个项之后的项”总为“\0"(即数据以一个‘\0’结尾),因此使用QByteArray比使用const char* 要方便的多。QByteArray还支持嵌入的“\0”字符,以允许我们存储任意的二进制数据。
QByteArray::data()或QByteArray::constdata()函数可以将QByteArry转换为const char*(字符串)。用法:
printf( "User: %s\n",str.toAscii().data() ); //str.toAscii()函数返回一个 QByteArray类型
3)变量类QVariant用来处理那些能够支持不同数据类型的变量,它像是常见Qt数据类型的一个共用体,在一个时间只保存一个单一类型。QVariant类可以支持许多Qt类型的值,如:基本的C++数据类型(如double和int),QColor,QPoint,QSize和QString。QVariant类也支持容器类,如QMap<QString,QVariant>,QList<QVariant>。
可以使用toT()函数将QVariant对象转换为T类型,并获取它的值(其中T代表一种数据类型)。这里的toT()函数会复制以前的QVariant对象,然后对其进行转换,所以以前的QVariant对象并不会改变。
在项目视图类、数据库模块和QSettings中广泛使用了变量。用法:
QMap<QString,QVariant> pearMap;
pearMap["Standard"] = 1.95;
pearMap["Organic"] = 2.25;
QMap<QString,QVariant>fruitMap;
fruitMap["Orange"] = 2.10;
fruitMap["Pineapple"] = 3.85;
fruitMap["Pear"] = pearMap;
这里我们利用字符串键和 浮点数或者映射 的值创建一个映射。顶级映射包含三个键:“Orange”、“Pear”和“ Pineapple”。与“Pear”键相关联的值是一个包含两个键(“Standard”和“Organic”)的映射。
上一篇:QT学习笔记(五)https://blog.csdn.net/weixin_44787158/article/details/99108517
下一篇:QT学习笔记(七)https://blog.csdn.net/weixin_44787158/article/details/99171781