Qt-Kenntnisse – Ein Überblick über Qt-Container

Überblick

Qt stellt eine universelle Containerklasse bereit, die auf der Vorlagenimplementierung basiert: Das Erreichen der Universalität hängt weitgehend von Vorlagenfunktionen ab . Der Container selbst dient zum Speichern von Daten. Bei den Daten handelt es sich hier jedoch hauptsächlich um Daten, die im Speicher gespeichert werden, wenn das Programm ausgeführt wird. Der Speicher wird nach Abschluss der Ausführung freigegeben.

Die integrierten Containerfunktionen von Qt : leicht , threadsicher und einfacher zu verwenden als STL .
Die Container von Qt werden implizit gemeinsam genutzt, sind reentrant und auf Geschwindigkeit, geringen Speicherverbrauch und minimale Inline-Codeerweiterung optimiert, was zu kleineren ausführbaren Dateien führt. Darüber hinaus sind sie insofern threadsicher, als sie von allen Threads, die auf sie zugreifen, als schreibgeschützte Container verwendet werden.

Qt unterstützt zwei Arten des Zugriffs auf Containerdaten und stellt Iteratoren im Java-Stil und im STL-Stil bereit. Die Java-Methode ist einfacher zu verwenden und hochfunktional. Gleichzeitig ist die STL-Methode auch sehr effizient und unterstützt STL-Algorithmen und Die Funktion von Das Kombinieren von Containern ist äußerst leistungsfähig.

Qt stellt außerdem das Schlüsselwort foreach bereit, um schnell den gesamten Container zu durchlaufen (ich verwende dieses häufig).

1. Containerklasse

Qt bietet sequentielle Container : QList, QLinkedList, QVector, QStack und QQueue. Für die meisten Anwendungsentwicklungen ist QList der beste Containertyp. QList ist eigentlich als Array-verknüpfte Liste konzipiert. Daher weist es eine sehr gute Effizienz auf, wenn am Anfang und Ende der verknüpften Liste gearbeitet wird. Wenn wir eine verknüpfte Liste im Zeigerformat benötigen, müssen wir QLinkedList verwenden. Die unterste Ebene von QVector ist ein Array, daher ist sein Speicher genauso kontinuierlich wie das Array. Häufig verwendete Datenergebnisse wie LIFO (Last In First Out) und FIFO (First In First Out) verfügen ebenfalls über entsprechende Container: QStack, QQueue, dh Stapel und Heap.

Qt bietet assoziative Container : QMap, QMultiMap, QHash, QMultiHash und QSet. Container mit „Multi“ können bequem mehrere Werte unterstützen, die einem einzigen Schlüssel zugeordnet sind. Hash-Container ermöglichen eine schnellere Suche nach sortierten Mengen, indem sie Hash-Funktionen anstelle von binären Suchvorgängen verwenden.
Die Containerübersichtstabelle ist unten aufgeführt

Art beschreiben
QList Diese Klasse wird am häufigsten verwendet. Die Containerparameter vieler Qt-APIs sind im Wesentlichen vom QList-Typ [da diese Klasse einen geringen Overhead hat und viele API-Standardparameter im Wesentlichen vom QList-Typ sind], da QList mithilfe eines Arrays implementiert wird. und es gibt eine Reihenfolge zwischen den Daten. Es unterstützt auch die Indizierung , wodurch sichergestellt wird, dass der indexbasierte Zugriff sehr schnell erfolgt. QList verfügt über alle Funktionen von Arrays, z. B. Hinzufügen, Löschen , Ändern und Suchen. QList erbt auch die Vor- und Nachteile von Arrays. [Vorteile: Bequeme Indizierung und einfaches Hinzufügen von Anfang und Ende; Nachteile: Unbequemes Einfügen bei großen Datenmengen]
QLinkedList Ähnlich wie bei QList, mit der Ausnahme, dass die Implementierungsmethode in Form einer Zeigerstruktur anstelle eines Arrays vorliegt, gibt es keinen Unterschied in der Verwendung. Jeder, der sich mit Datenstrukturen befasst hat, weiß, was die Merkmale einer verknüpften Zeigerliste sind: schnelles Einfügen und langsames Einfügen Durchquerung [Alle sind Daten, wenn die Menge groß ist]
QVector Merkmale: Der Knotenspeicher ist kontinuierlich, die Vor- und Nachteile sind die gleichen wie bei Arrays, aber die Funktionskapselung ist leistungsfähiger als bei Arrays.
QStack Tatsächlich handelt es sich um eine Unterklasse von QVector, die die lokalen Funktionen von QVector verwendet, dh die Eigenschaften des Stapels (LIFO) verwendet.
QQueue Dies ist eine Unterklasse von QList. Sie verwendet die Teilfunktion von QList, die die Warteschlangenfunktion (FIFO) verwendet.
QSet Eigenschaften von Teilmengen: Eine Kopie des Arrays bleibt global erhalten und Duplikate können entfernt werden.
QMap<Schlüssel, T> Dadurch wird ein Wörterbuch (assoziatives Array) bereitgestellt, das Schlüssel vom Typ Key Werten vom Typ T zuordnet. Normalerweise ist jeder Schlüssel einem einzelnen Wert zugeordnet. QMap speichert seine Daten in der Schlüsselreihenfolge; wenn die Reihenfolge keine Rolle spielt, ist QHash eine schnellere Option. QMap sortiert Schlüssel und greift auf Werte nach Schlüssel zu.
QMultiMap<Key, T> Im wahrsten Sinne des Wortes kann eine Taste mehreren Werten entsprechen, genau wie ein Signal mehreren Slot-Funktionen zugeordnet werden kann, eins zu viele
QHash<Schlüssel, T> Die Funktion ähnelt auch QMap, mit der Ausnahme, dass der Schlüsselwertabgleich durch den Hash-Algorithmus erreicht wird und der Abgleich schneller ist , die Schlüssel jedoch ungeordnet angeordnet sind.
QMultiHash<Schlüssel, T> Es hat die gleiche Funktion wie QMultiMap, die Implementierungsmethode ist jedoch unterschiedlich und verfügt über einen eigenen Anpassungsbereich.

Hinweis:
In verschiedenen Containern gespeicherte Werte können von jedem zuweisbaren Datentyp sein. Qualifizierende Datentypen müssen Kopierkonstruktoren und Zuweisungsoperatoren bereitstellen. Für einige Operationen ist außerdem ein Standardkonstruktor erforderlich. Dies deckt die meisten Datentypen ab, die in Containern gespeichert werden können, einschließlich Basistypen (wie int und double usw.), Zeigertypen und Qt-Datentypen (wie QString, QDate, QTime usw.). Es enthält jedoch weder QObject noch einen QObject-Unterklassentyp wie (QWidget, QDialog, QTimer usw.). Wenn Sie versuchen, eine QList<QWidget> zu instanziieren, deaktiviert der Compiler den Kopierkonstruktor und den Zuweisungsoperator von QWidget und der Container wird ungültig. Qt hat sich einen anderen Weg für uns ausgedacht: Speichern Sie die Adressen dieser Objekttypen in Containern, dh als Zeigertypen wie QList<QWidget *>, damit wir unseren Zweck erreichen können.

//不能使用这种方式
QList <QWidget> widgets;

//而是这样
QList<QWidget *> ptr_widgets;

2. Iterator

Iteratoren bieten eine einheitliche Möglichkeit, auf Elemente in einem Container zuzugreifen. Die Containerklassen von Qt stellen zwei Arten von Iteratoren bereit: Iteratoren im Java-Stil und Iteratoren im STL-Stil.

1. Java-Stil

Iteratoren im Java-Stil wurden in Qt 4 eingeführt, und dieser Stil ist bereits der Standard-Iterator, der in Qt-Anwendungen verwendet wird. Sie sind einfacher zu verwenden als Iteratoren im STL-Stil, allerdings etwas weniger effizient.
Für jede Containerklasse gibt es zwei Iterator-Datentypen im Java-Stil:

  • Einer bietet schreibgeschützten Zugriff
  • Ein anderer ermöglicht Lese- und Schreibzugriff
Containername schreibgeschützter Iterator Iterator lesen und schreiben
QList, QQueue QListIterator QMutableListIterator
QLinkedList QLinkedListIterator QMutableLinkedListIterator
QVector, QStack QVectorIterator QMutableVectorIterator
QSet QSetIterator QMutableSetIterator
QMap<Schlüssel, T>, QMultiMap<Schlüssel, T> QMapIterator<Schlüssel, T> QMutableMapIterator<Key, T>
QHash<Schlüssel, T>, QMultiHash<Schlüssel, T> QHashIterator<Key, T> QMutableHashIterator<Key, T>

Wenn Sie lernen, sollten Sie sich auf das Erlernen von QList und QMap konzentrieren. Die Iteratortypen von QLinkedList, QVector und QSet haben genau die gleiche Schnittstelle wie die Iteratoren von QList; ebenso haben die Iteratortypen von QHash die gleiche Schnittstelle wie die Iteratoren von QMap.
Im Gegensatz zu Iteratoren im STL-Stil (unten beschrieben) verweisen Iteratoren im Java-Stil zwischen Elementen und nicht direkt auf Elemente. Sie zeigen also entweder auf den Anfang des Containers (vor dem ersten Element), oder auf das Ende des Containers (nach dem letzten Element) oder zwischen zwei Elementen. Das folgende Bild zeigt die gültigen Iteratorpositionen für eine Liste mit vier Elementen als rote Pfeile:
Fügen Sie hier eine Bildbeschreibung ein

1. QListIterator

Wie im folgenden Code: Iterator i ist ein schreibgeschützter Iterator und die initialisierte Position der Vorwärtsdurchquerung liegt vor A

//正向遍历
QList<QString> list;
list << "A" << "B" << "C" << "D";

QListIterator<QString> i(list);
while (i.hasNext())
	qDebug() << i.next();

//反向遍历
QListIterator<QString> i(list);
i.toBack();
while (i.hasPrevious())
	qDebug() << i.previous();

Schematische Darstellung einiger Methoden Die
Fügen Sie hier eine Bildbeschreibung ein
folgende Tabelle fasst die QListIterator-API zusammen:

Funktion erklären
nach vorne() Verschieben Sie den Iterator an die erste Position des iterierbaren Objekts (an die Vorderseite des Knotens).
unterstützen() Verschieben Sie den Iterator an die letzte Position des Iterables (am Ende des Knotens).
hasNext() Bestimmen Sie, ob ein iterierbares Objekt ein nächstes Objekt hat
nächste() Gibt das nächste Objekt zurück und verschiebt den Iterator an die nächste Position
peekNext() Gibt das nächste Objekt zurück, ohne den Iterator zu verschieben
hasPrevious() Bestimmen Sie, ob ein iterierbares Objekt ein vorheriges Objekt hat
vorherige() Gibt das vorherige Objekt zurück und verschiebt den Iterator an die vorherige Position
peekPrevious() Gibt das vorherige Objekt zurück, ohne den Iterator zu verschieben

Der Name dieser Funktion ist eigentlich sehr klar. Sie wissen wahrscheinlich, was er bedeutet. Wenn Sie es wirklich nicht wissen, schauen Sie in der Hilfedokumentation nach.

2. QMutableListIterator

Wenn Sie das iterierbare Objekt ändern müssen, müssen Sie QMutableListIterator verwenden, um es abzuschließen

//删除节点
QMutableListIterator<int> i(list);
while (i.hasNext()) {
    
    
	if (i.next() % 2 != 0)
		i.remove();
}

//修改节点值 一
QMutableListIterator<int> i(list);
while (i.hasNext()) {
    
    
	if (i.next() > 128)
		i.setValue(128);
}

//修改节点值 二
QMutableListIterator<int> i(list);
while (i.hasNext())
	i.next() *= 2;

3. QMapIterator

Wir wenden uns nun QMapIterator zu, das ein wenig anders ist, da es (Schlüssel-Wert-)Paare iteriert. Wie QListIterator bietet QMapIterator ähnliche Funktionen wie QListIterator, z

  • nach vorne()
  • unterstützen()
  • hasNext()
  • nächste()
  • peekNext()
  • hasPrevious()
  • vorherige()
  • peekPrevious().
    Der Unterschied besteht jedoch darin, dass das vom Iterator beim Aufruf von next(), peekNext(), previous() oder peekPrevious() zurückgegebene Objekt der Typ des Schlüssel- Wert- Paares ist. Wir können die Funktionen key() und value() aufrufen Holen Sie sich das Schlüssel-Wert-Paar. Wert.
QMap<QString, QString> map;
map.insert("Paris", "France");
map.insert("Guatemala City", "Guatemala");
map.insert("Mexico City", "Mexico");
map.insert("Moscow", "Russia");

QMapIterator<QString, QString> i(map);
while (i.hasNext()) {
    
    
	qDebug()<<"key:"<<i.next().key()<<" value: "<<i.next().value();
}

4. QMutableMapIterator

Die Besonderheit besteht darin, dass es lesen und schreiben kann und auch mit Iteratoren desselben Typs verwendet werden kann.

//删改示例
QMap<QString, QString> map;
map.insert("Paris", "France");
map.insert("Guatemala City", "Guatemala");
map.insert("Mexico City", "Mexico");
map.insert("Moscow", "Russia");

QMutableMapIterator<QString, QString> i(map);
while (i.hasNext()) {
    
    
	if (i.next().key().endsWith("City"))
		i.remove();
}

//QHash和QMap交互
QMap<int, QWidget *> map;
QHash<int, QWidget *> hash;

QMapIterator<int, QWidget *> i(map);
while (i.hasNext()) {
    
    
	i.next();
	hash.insert(i.key(), i.value());
}

2. STL-Stil

Iteratoren im STL-Stil sind seit der Veröffentlichung von Qt 2.0 verfügbar. Sie sind mit den gängigen Algorithmen von Qt und STL kompatibel und auf Geschwindigkeit optimiert.
Für jede Containerklasse gibt es zwei Iteratortypen im STL-Stil:

  • Einer bietet schreibgeschützten Zugriff
  • Ein anderer ermöglicht Lese- und Schreibzugriff

Der formale Unterschied besteht darin, ob das Präfix const_ hinzugefügt wird oder nicht.
Nach Möglichkeit sollten schreibgeschützte Iteratoren verwendet werden, da sie schneller sind als Iteratoren mit Lese-/Schreibzugriff.

Containername schreibgeschützter Iterator Iterator lesen und schreiben
QList, QQueue QList::const_iterator QList::iterator
QLinkedList QLinkedList::const_iterator QLinkedList::iterator
QVector, QStack QVector::const_iterator QVector::iterator
QSet QSet::const_iterator QSet::iterator
QHash<Key, T>, QMultiHash<Key, T> QHash<Key, T>::const_iterator QHash<Key, T>::iterator

STL 迭代器的 API 以数组中的指针为模型。例如,++ 运算符将迭代器前进到下一个项,* 运算符返回迭代器指向的项。实际上,对于将项目存储在相邻内存位置的 QVector 和 QStack,迭代器类型只是 T * 的 typedef,而 const_iterator 类型只是 const T * 的 typedef。
重点介绍 QList 和 QMap。QLinkedList,QVector和QSet的迭代器类型与QList的迭代器具有完全相同的接口;同样,QHash 的迭代器类型与 QMap 的迭代器具有相同的接口。

1. QList-iterator

迭代器指向的节点值就是一个一个节点类型。

QList<QString> list;
list << "A" << "B" << "C" << "D";

QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
	*i = (*i).toLower();

Java 风格的迭代器不同,STL 样式的迭代器直接指向项。 容器的 begin() 函数返回指向容器中第一项的迭代器。容器的 end() 函数将迭代器返回到虚构项,该项的位置比容器中的最后一项晚一个位置。end() 标记无效仓位;它绝不能被取消引用。它通常用于循环的中断条件。如果列表为空,begin() 等于 end(),因此我们从不执行循环。
Fügen Sie hier eine Bildbeschreibung ein
在迭代时,关于迭代的item如何获取,以及如何跨步长来获取,看下表

操作 含义
*i 返回当前节点
++i 迭代器后移一位
i += n 迭代器后移 n 位
–i 迭代器前移一位
i -= n 迭代器前移 n 位
i - j 两个迭代器直接的索引差

2. QMap- iterator

对于QMap和QHash,*i 返回项的值和Java-style迭代器返回是一样的 键值对 。要获取迭代器节点的键 需要调用key()。同样的迭代器类型还提供了一个value()函数来获取值。其实很像QList里面存放结构体类型一样。

QMap<int, int> map;

QMap<int, int>::const_iterator i;
for (i = map.constBegin(); i != map.constEnd(); ++i)
	qDebug() << i.key() << ':' << i.value();

3. 隐式共享的问题

  • 当迭代器在容器上处于活动状态时,应该避免复制该容器【例子一】
  • 我们应该始终获取容器的副本,并对副本进行迭代【例子二】

例子一:
像这样的写法 不对

QVector<int> a, b;
a.resize(100000); // make a big vector filled with 0.

QVector<int>::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;

a[0] = 5;

b.clear(); // Now the iterator i is completely invalid.

int j = *i; // Undefined behavior!

最好改为这样的

QVector<int>::iterator i = a.begin();
b = a;

QVector<int>::iterator i = a.begin();

a[0] = 5;

b.clear(); 

int j = *i; 

例子二

// RIGHT
const QList<int> sizes = splitter->sizes();
QList<int>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)

// WRONG
QList<int>::const_iterator i;
for (i = splitter->sizes().begin();
	i != splitter->sizes().end(); ++i)

三、foreach 关键字

如果你只想按顺序遍历容器中的所有项,你可以使用Qt的foreach关键字。该关键字是c++语言中特定于qt的附加内容,是使用预处理器实现的。

用法很简单:

foreach (variable, container)
{
//操作
}
操作里面包括:获取值节点,break退出循环 都可以

1. QList类型

//例子一
QLinkedList<QString> list;
QString str;

foreach (str, list)
	qDebug() << str;

//例子二
QLinkedList<QString> list;
  foreach (const QString &str, list)
      qDebug() << str;
    
//例子三
QLinkedList<QString> list;
  foreach (const QString &str, list) {
    
    
      if (str.isEmpty())
          break;
      qDebug() << str;
  }

2. QMap类型

直接上例子。没啥特殊的

QMap<QString, int> map;
  foreach (const QString &str, map.keys())
      qDebug() << str << ':' << map.value(str);

//多键值对映射时
QMultiMap<QString, int> map;
  foreach (const QString &str, map.uniqueKeys()) {
    
    
      foreach (int i, map.values(str))
          qDebug() << str << ':' << i;
  }

当Qt进入foreach循环时,它会自动获取容器的副本【也就是会拷贝一份副本】。如果你在迭代的时候修改了容器,那不会影响循环。(如果不修改容器,复制仍然会发生,但是由于Qt使用了隐式共享,复制容器的操作非常快)
Qt的foreach循环的替代方法是基于范围的for,它是C++ 11和更新版本的一部分。但是,请记住,基于范围的for可能会强制分离Qt容器,而foreach不会。

四、其他类似容器类

Qt包括三个模板类,在某些方面类似于容器。这些下面这些类不提供迭代器,不能与foreach关键字一起使用。

变量名 描述
QVarLengthArray<T,Prealloc > 提供了一个低级的可变长度数组。在速度特别重要的地方可以代替QVector使用。
QCache<Key,T > 提供了一个缓存来存储与Key类型的键相关联的某个T类型的对象。
QContiguousCache < T > 提供了一种高效的方法来缓存通常以连续方式访问的数据。
QPair<T1,T2 > 存储一对元素

Qt的中存在的非通用模板类型有QBitArray、QByteArray、QString和QStringList。这些类存储的都是特定类型的item,虽然能使用迭代器的方法,但是已经失去通用容器的意义了。

五、算法复杂度

算法复杂性关注的是随着容器中项目数量的增加,每个函数的速度有多快(或多慢)。例如,在QLinkedList的中间插入一个项目是一个非常快速的操作,与QLinkedList中存储的项目数量无关。另一方面,如果QVector包含许多项,那么在QVector中间插入一个项开销可能非常大,因为必须将一半的项在内存中移动一个位置。

“Amort ”代表“分摊行为”。比如“Amort.O(1)”意味着如果只调用该函数一次,可能会得到O(n)行为,但是如果调用它多次(例如,n次),平均行为将是O(1)。

Qt的序列式容器复杂度

变量名 查找 插入 头部插入 尾部插入
QLinkedList O(n) O(1) O(1) O(1)
QList O(1) O(n) Amort. O(1) Amort. O(1)
QVector O(1) O(n) O(n) Amort. O(1)

Qt的关联式容器以及集合复杂度

变量名 键 查找平均情况 键 查找最坏情况 插入平均情况 插入最坏情况
QMap<Key, T> O(log n) O(log n) O(log n) O(log n)
QMultiMap<Key, T> O(log n) O(log n) O(log n) O(log n)
QHash<Key, T> Amort. O(1) O(n) Amort. O(1) O(n)
QSet Amort. O(1) O(n) Amort. O(1) O(n)

六、增长策略

1. 连续地址分配容器

QVector、QString和QByteArray在内存中连续存储它们的项;QList维护一个指向其存储的项的指针数组,以提供基于索引的快速访问(除非T是指针类型或指针大小的基本类型,在这种情况下,值本身存储在数组中);QHash<Key,T >保存一个哈希表,其大小与哈希中的项目数成比例。
为了避免每次在容器末尾添加项目时都重新分配数据,这些类通常会分配多的一些的内存。

假设我们向QString字符串追加了15000个字符。那么当QString用尽空间时,将发生以下18次重新分配(总共可能有15000次): 4、8、12、16、20、52、116、244、500、1012、2036、4084、6132、8180、10228、12276、14324、16372。最后,QString分配了16372个Unicode字符,其中15000个被占用。
上面的值可能看起来有点奇怪,但以下是指导原则:

  • 0 - 20:QString一次分配4个字符,直到它的大小达到20
  • 20 - 4084:它每次都以两倍的大小前进
  • 4084 - 更大,它按2048个字符(4096字节)的块前进。这是有意义的,因为现代操作系统在重新分配缓冲区时不会复制整个数据;物理内存页面被简单地重新排序,实际上只需要复制第一页和最后一页上的数据

QByteArray 和 QList跟QString在分配上是大同小异的。
但是若 QVector 也将该算法用于可以使用memcpy()在内存中移动的数据类型(包括基本的C++类型、指针类型和Qt的共享类),但对只能通过调用 复制构造函数 和 析构函数 来移动。由于在这种情况下重新分配的成本更高,所以QVector通过在空间用尽时总是将内存加倍来减少重新分配的次数

2. 关联容器

QHash<Key, T> ist ein völlig anderer Fall. QHash wächst intern exponentiell mit einer Potenz von 2 in Form einer Hash-Tabelle. Jedes Mal, wenn es wächst, wird der Projektknoten in den neuen Zuordnungsbereich verschoben. Die Berechnungsmethode ist qHash(key) % QHash::capacity() Hash Tischkapazität). Dies gilt auch für QSet und QCache<Key, T>.
Bei den meisten Anwendungen kann der von Qt bereitgestellte Standardwachstumsalgorithmus dieses Problem lösen. Wenn Sie mehr Kontrolle benötigen, bieten QVector, QHash<Key, T>, QSet, QString und QByteArray drei Funktionen, mit denen Sie überprüfen und angeben können, wie viel Speicher zum Speichern von Elementen verwendet werden soll:

Funktionsname beschreiben
Kapazität() Gibt den von allen Knoten im Container belegten Platz zurück
Reserve (Größe) Explizite vorab zugewiesene Speichergröße für den Container
quetschen() Geben Sie ungenutzten Containerraum frei und reduzieren Sie die Größe des Containers

Ich denke du magst

Origin blog.csdn.net/qq_43680827/article/details/123413256#comments_28562389
Empfohlen
Rangfolge