一篇看懂QVector

簡介

 QVector是Qt的一個通用容器類。
 它將其項存儲在相鄰的內存位置,並提供快速地、基於索引的訪問(QVector可以看做是一個封裝了一個數組的模板類[],它內部維護一個數組,並且提供給外部一些訪問的方法)。

QList/QLinkedList/QVector/QVarLengthArray選擇討論

 QList/QLinkedList/QVector/QVarLengthArray提供了類似的api和功能。它們通常可以互換,但在性能方面有所不同。下面有一些使用建議:

  1. 如果你不清楚用哪種容器,那麼建議你使用QVector。QVector通常比QList有更好的性能,因爲QVector總是將它的項按順序存儲在內存中,而QList在堆上分配項,除非sizeof(T)<=sizeof(void*),並且T已經使用Q_DECLARE_TYPEINFO聲明爲Q_MOVABLE_TYPE或Q_PRIMITIVE_TYPE。參閱QList優缺點。
  2. 不過QList在Qt API中普遍被用來傳遞參數和返回值。與這些API交互時,使用QList。
  3. 如果你確實需要一個真正的鏈表,它可以保證在鏈表中插入耗時爲常數時間。並對項使用迭代器(iterator)而不是索引,那麼你可以使用QLinkedList。

Note:QVector和QVarLengthArray都保證了兼容C語言的數組佈局。但是QList沒有這麼做。如果你的應用程序必須與C API進行對接,那麼這點可能需要注意。
Note:只要引用的項仍然在容器中,那麼QLinkedList中的迭代器和QList分配在堆上的引用(heap-cllocating)將會一直存在。對QVector中的迭代器和引用和非堆分配的QList(non-heap-allocating)來說,情況相反。

QVector簡單用法

 下面是一個存儲整數的QVector和存儲QString的QVector的一個示例。

	QVector<int> integerVector;
	QVector<QString> stringVector;

 QVector將它的項存儲在vector(數組)中。通常,vector在創建時會帶一個初始大小,下面代碼是一個包含200個元素的QVector:

	QVector<QString> vector(200);

 vector中的元素會被自動初始化成一個默認構造值(default-constructed value)。如果你想要使用其他的值初始化vector,就通過第二個參數將其傳遞給構造函數。

	QVector<QString> vector(200, "Pass");

 你還可以使用fill()來用某個值填充vector。
 QVector使用從0開始(0-based)的索引,就像c++數組一樣。要訪問特定索引位置的項,可以使用操作符[]()。在非常量vector中,操作符[]()返回一個能被用作左值的引用:

	if (vector[0] == "Lix")
		vector[0] = "xxx";

 如果是隻讀訪問(read-only),可以使用另一種寫法at():

	for (int i = 0; i < vector.size(); ++i) {
      if (vector.at(i) == "Alfonso")
          cout << "Found Alfonso at position " << i << endl;
	}

 at()比操作符執行更快,因爲他不會進行深拷貝(deep copy)。
 訪問存儲在QVector的數據還可以使用data()。該函數返回指向vector中第一項的指針,你可以使用這個指針直接訪問和修改vector中的元素。如果你需要將QVector傳遞給一個接受C++數組的函數,這個指針會很有用。
 如果你想要查找vector中某個特定的值,使用indexOf()或者lastIndexOf()。前者從指定索引位置開始向前搜索,後者向後搜索。若找到匹配項,則返回匹配項索引。否則返回-1,例如:

	int i = vector.indexOf("harumi");
	if (i != -1)
		cout << "First occurrence of Harumi is at position"<< i <<endl;

 如果只是想檢查vector是否包含某個值,使用contains()。如果想知道某個值在vector中出現的次數,可以使用count()。
 QVector提供了增刪查改函數:insert()/replace()/remove()/prepend()/append()。對於存儲量大的vector,這些函數可能會比較慢(線性時間-linear time),除了append()和replace(),因爲它們需要將vector中的許多項在內存中移動一個位置(這點可以參考數組,因爲是連續的順序表,若要在中間插入或移除數據,許多元素都得移動)。如果你想要一個提供快速插入、刪除的容器類,那麼可以使用QList或者QLinkedList。
 和普通的c++函數不同,QVector可以通過調用resize()隨時調整大小。如果新的大小大於舊的大小,QVector可能需要重新分配整個vector。QVector試圖通過預分配兩倍於實際數據需要的內存來減少重新分配。
 如果你預先知道QVector包含多少項,那麼你可以調用reserve(),要求QVector預先分配一定數量的內存。你還可以調用capacity()來查明QVector實際分配了多少內存。
Note:使用非const操作符和函數會導致QVector對數據進行深拷貝,這是由於隱式共享(implicit sharing)。
 QVector存儲的值類型必須是可分配的數據類型。這涵蓋了大多數的數據類型,但是編譯器不允許你將QWidget存儲爲值。取而代之的是,你可以存一個QWidget *。一些函數有額外的要求,例如,indexOf()和lastIndexOf()希望值類型支持==()運算符。這些需求是在每個函數的基礎上進行編寫的。
 和其他的容器一樣,QVector提供了java風格的迭代器(QVectorIterator和QMutableVectorIterator)和stl風格的iterator(QVector::const_iterator和QVector::iterator)。不過在實際運用中,很少用到,因爲你可以在QVector中使用索引。
 除了QVector,Qt也提供QVarLengthArray,這是一個非常低級的類,幾乎沒有優化。
 QVector不支持inserting、prepending、replacing它自己的值。如果這麼做會引起應用程序報錯而中止。

實際運用注意

Qt容器內存釋放

  1. QVector::clear(),在Qt5.6之前,它會釋放vector中的內存。但是從5.7開始,它的容量(QVector中維護的數組的空間)會被保留。要釋放所有的容量,你可以這麼做:

       QVector<T> v ...;
       QVector<T>().swap(v);
       Q_ASSERT(v.capacity() == 0);
    

    或者使用squeeze()

    	v.clear();
    	v.squeeze();
    

    這點很重要,在QVector中添加元素,如果只是調用clear,不釋放空間,那之後的使用過程中,它一直會佔着容器中最大佔用量時的空間。(第一次往容器中添加了100個元素,然後clear了,你第二次添加了50個元素,此時容器內數組還佔着100個元素的空間,用vector.capacity()可以檢驗)

  2. QVector存儲的類型爲指針的情況下,當QVector銷燬時,存儲的指針指向的空間並不會被釋放,需要手動釋放那些空間。比如可以這樣使用:

    	for(int i = 0; i < v.size(); i++)
    	{
    		if (v[i] != nullptr)
    		{
    			delete v[i];
    			v[i] = nullptr;
    		}
    	}
    	v.clear();
    	v.squeeze();
    

    不過,Qt中提供了qDeleteAll專門用於清除容器的內存。可以往裏面傳入首尾迭代器或者容器來完全釋放容器內存。

總結

 QVector是Qt編程中常用的容器,對它有個大致的認識很重要。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章