vector的resize函數和reserve函數

對於C++的vector容器模板類,存在size和capacity這樣兩個概念,可以分別通過vector的size()和capacity()方法獲得該vector當前的size和capacity值。相應的,vector提供了兩個方法來分別對size和capacity進行操作,它們就是resize方法和reserve方法。


首先,對於size和capacity,這是兩個比較容易混淆的概念。都說要抱着問題來學習,才能做到事半功倍。那麼,這裏便提出三個問題:什麼是vector的大小(即size)?什麼是vector的容量(即capacity)?這兩個概念的區別在哪裏?


對於抽象的問題,只要我們把它們同我們的生活實際相結合,將問題具象化,自然就會很好的理解。

就拿我們的辦公室舉例,假設,我們部門的辦公地點位於公司大樓的六樓。在我們的辦公室裏面,放置了100套辦公桌椅(工位),公司說按照一個蘿蔔一個坑來算,你們部門最多隻能招這麼多人,那麼,這時我們可以說,我們部門的容量(即capacity)就是100人,如果我們部門是公司剛成立的部門,正處於發展壯大的階段,目前只有40爲員工,也就是說,辦公室裏只坐了40個人,另外60個工位是空着的,那麼,我們可以說,我們部門當前的大小(即size)是40人。這實際上就是size和capacity的區別。類比到vector,size和capacity的概念自然就很清楚了。


cplusplus.com中對capacity是這樣定義的:

This capacity is not necessarily equal to the vector size. It can be equal or greater, with the extra space allowing to accommodate for growth without the need to reallocate on each insertion.

一個allowing道出了真諦!這裏還要區分兩個概念,就是:爲vector分配的存儲空間和vector的大小是兩個不同的概念。爲vector分配的存儲空間,實際上就是capacity,指的是當前vector最多能使用的存儲空間,是大於等於vector的大小的,當vector實際需要使用的存儲空間大於當前分配給它的存儲空間時,需要重新爲其分配存儲空間。


cplusplus.com中對size的定義是:

This is the number of actual objects held in the vector, which is not necessarily equal to its storage capacity.

實際上就是vector中當前實際存儲的元素個數。


弄清楚了size和capacity這兩個概念之後,對於resize和reserve兩個方法就很好理解了。

cplusplus.com中對reserve的定義是:

Request a change in capacity
Requests that the vector capacity be at least enough to contain n elements.

If n is greater than the current vector capacity, the function causes the container to reallocate its storage increasing itscapacity to n (or greater).

In all other cases, the function call does not cause a reallocation and the vector capacity is not affected.

This function has no effect on the vector size and cannot alter its elements.


從上面的說明中,可以得到以下信息:

1、reserve方法被用來重新分配vector的容量大小;

2、只有當所申請的容量大小n大於vector的當前容量時,纔會重新爲vector分配存儲空間;

3、reserve方法對於vector的大小(即size)沒有任何影響;


具體通過下面的例子驗證

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     vector<int> vect;  
  9.   
  10.     vect.reserve(5);    // 調用reserve方法爲vect分配容量(即存儲空間)  
  11.   
  12.     vect.push_back(1);  
  13.     vect.push_back(2);  
  14.     vect.push_back(3);  
  15.     vect.push_back(4);  // 插入4個元素  
  16.   
  17.     cout << vect.size() << endl;      // vect的實際大小(即包含多少元素)  
  18.     cout << vect.capacity() << endl;     // vect的容量大小  
  19.   
  20.     return 0;  
  21. }  

結果爲



從結果中,就可以很清楚的看到capacity和size的區別。


下面看一個綜合的例子,可以從中獲得很多信息:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     vector<int> vect;  
  9.   
  10.     vect.reserve(5);  
  11.   
  12.     vect.push_back(1);  
  13.     vect.push_back(2);  
  14.     vect.push_back(3);  
  15.     vect.push_back(4);  
  16.   
  17.     cout << vect.size() << endl;  
  18.     cout << vect.capacity() << endl;  
  19.   
  20.     vect.push_back(5);                                 
  21.     vect.push_back(6);                       // 插入兩個元素,此時vect的大小大於之前分配的容量5  
  22.       
  23.     cout << "size1 = " << vect.size() << endl;   
  24.     cout << "capacity1 = " << vect.capacity() << endl;  
  25.   
  26.     vect.push_back(7);  
  27.     vect.push_back(8);                       // 在插入兩個元素,和上面的結果進行對比,會有意外收穫  
  28.     cout << "size1_1 = " << vect.size() << endl;   
  29.     cout << "capacity1_1 = " << vect.capacity() << endl;  
  30.   
  31.     vect.reserve(3);                        // 當程序執行到此處時,vect的容量大小一定是大於3的  
  32.   
  33.     cout << "size2 = " << vect.size() << endl;  
  34.     cout << "capacity2 = " << vect.capacity() << endl;  
  35.   
  36.     vect.reserve(12);  
  37.   
  38.     cout << "size3 = " << vect.size() << endl;  
  39.     cout << "capacity3 = " << vect.capacity() << endl;  
  40.   
  41.     return 0;  
  42. }  

執行結果爲:



對這一執行結果,一點點進行分析:

1、首先,看結果size1和capacity1,在打印這兩個結果前,程序向vect中插入了兩個元素,之前,vect中存在4個元素且容量爲5,按照我之前的設想,如果我採用push_back向vect中插入元素時,當元素數量大小capacity時,vect的capacity會隨着size變大而變大,但應該是和size相等。但此處,vect的大小爲6,但是容量卻是7,且這個7,來的很是突然,我往哪個方面靠都靠不上啊。好吧,先把這個疑問姑且放下,現在我們猜想,是不是push_back中針對這種情況會有處理,始終保持vect的capacity比size至少大1,帶着這個猜想繼續向下看,我又向vect中插入了兩個元素,此時,vect的大小爲8,若是我們剛纔的猜想是正確的話,則此時,vect的capacity應該增大爲9了,但是此時結果size1_1和capacity1_1卻給了我當頭一棒,這個10又是怎麼回事?學習編程永遠記住一件事情,所有問題的答案,都能從代碼中找到。於是,我順着vector的push_back源碼開始找下去,就有了下面這段追蹤代碼

[cpp] view plain copy
  1.    ...  
  2. void push_back(_Ty&& _Val)  
  3.    ...  
  4.     if (this->_Mylast == this->_Myend)  
  5.     _Reserve(1);  
  6.    ...  
  7.   
  8. void _Reserve(size_type _Count)  
  9. {   // ensure room for _Count new elements, grow exponentially  
  10.     size_type _Size = size();  
  11.     if (max_size() - _Count < _Size)  
  12.         _Xlen();  
  13.     else if ((_Size += _Count) <= capacity())  
  14.         ;  
  15.     else  
  16.         reserve(_Grow_to(_Size));  
  17. }  
  18.   
  19. size_type _Grow_to(size_type _Count) const  
  20. {   // grow by 50% or at least to _Count  
  21.     size_type _Capacity = capacity();  
  22.   
  23.     _Capacity = max_size() - _Capacity / 2 < _Capacity  
  24.         ? 0 : _Capacity + _Capacity / 2;    // try to grow by 50%  
  25.     if (_Capacity < _Count)  
  26.         _Capacity = _Count;  
  27.     return (_Capacity);  
  28. }  
由這段官方實現代碼,終於找到了答案,原來在使用push_back向vect中插入元素時,如果當前元素數量大於vector的capacity時,會重新爲vector分配存儲空間,而分配的原則就是:

                    原capacity + 原capacity / 2

這樣,就解釋了上面的7和10兩個結果,最初,vect的容量大小爲5,在第一次插入兩個元素後,vector中的元素數量大於5了,所以此時,會重新爲vect分配容量,分配大小爲5 + 5 / 2 = 7,而同樣的,第二次重新分配vect的容量是7 + 7 / 2 = 10,這就合理的解決了剛纔的疑問。


2、當vect的容量大小爲10時,再調用reserve方法,重新爲其設置容量爲3時,不會進行任何操作,可以看到,vect的容量大小還是10;

3、當爲vect設置容量大小爲12時,可以看到,成功的改變了vect的容量大小;

4、不論哪次調用reserve方法,vect的size大小在調用前後,始終沒有被改變過。


到此,是我對reserve方法的一些思考和驗證。


cplusplus.com中對resize的定義是:

Resizes the container so that it contains n elements.

If n is smaller than the current container size, the content is reduced to its first n elements, removing those beyond (and destroying them).

If n is greater than the current container size, the content is expanded by inserting at the end as many elements as needed to reach a size of n. If val is specified, the new elements are initialized as copies of val, otherwise, they are value-initialized.

If n is also greater than the current container capacity, an automatic reallocation of the allocated storage space takes place.

Notice that this function changes the actual content of the container by inserting or erasing elements from it.


從上述說明中,可以得到下面的信息:

1、resize方法被用來改變vector的大小,即vector中元素的數量,我們可以說,resize方法改變了容器的大小,且創建了容器中的對象;

2、如果resize中所指定的n小於vector中當前的元素數量,則會刪除vector中多於n的元素,使vector得大小變爲n;

3、如果所指定的n大於vector中當前的元素數量,則會在vector當前的尾部插入適量的元素,使得vector的大小變爲n,在這裏,如果爲resize方法指定了第二個參數,則會把後插入的元素值初始化爲該指定值,如果沒有爲resize指定第二個參數,則會把新插入的元素初始化爲默認的初始值;

4、如果resize所指定的n不僅大於vector中當前的元素數量,還大於vector當前的capacity容量值時,則會自動爲vector重新分配存儲空間;


還是通過代碼來說話:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     vector<int> vect;  
  9.     int i = 0;  
  10.   
  11.     vect.reserve(10);  
  12.   
  13.     vect.push_back(1);  
  14.     vect.push_back(2);  
  15.     vect.push_back(3);  
  16.     vect.push_back(4);  
  17.     vect.push_back(5);  
  18.     vect.push_back(6);  
  19.     vect.push_back(7);  
  20.     vect.push_back(8);                               // 此時vect的size大小爲8  
  21.   
  22.     cout << vect.size() << endl;  
  23.     cout << vect.capacity() << endl;  
  24.   
  25.     vect.resize(6);                                   // 此處設置vect的大小比當前vect中元素數量小,且沒有指定初始化值  
  26.   
  27.     cout << "size1 = " << vect.size() << endl;  
  28.     cout << "capacity1 = " << vect.capacity() << endl;  
  29.   
  30.     for (i = 0 ; i < vect.size(); i++)  
  31.     {  
  32.         cout << vect[i] << endl;  
  33.     }  
  34.   
  35.     vect.resize(4, 10);                              // 此處設置vect的大小爲4,比前面的6小,且指定了初始化值,看是否會改變前四個元素的值  
  36.   
  37.     cout << "size1_1 = " << vect.size() << endl;  
  38.     cout << "capacity1_1 = " << vect.capacity() << endl;  
  39.   
  40.     for (i = 0 ; i < vect.size(); i++)  
  41.     {  
  42.         cout << vect[i] << endl;  
  43.     }  
  44.   
  45.     vect.resize(8, 7);                         // 此處設置vect的大小爲8,大於當前vect的大小4,但是小於vect的當前容量10,指定初始化值爲7  
  46.   
  47.     cout << "size2= " << vect.size() << endl;  
  48.   
  49.     cout << "capacity2 = " << vect.capacity() << endl;  
  50.   
  51.     for (int i = 0 ; i < vect.size(); i++)  
  52.     {  
  53.         cout << vect[i] << endl;  
  54.     }  
  55.   
  56.     vect.resize(10);                           // 此處設置vect的大小爲10,大於當前vect的大小8,但是等於vect的當前容量10,沒有指定初始化值,採用默認值  
  57.   
  58.     cout << "size3 = " << vect.size() << endl;  
  59.     cout << "capacity3 = " << vect.capacity() << endl;  
  60.   
  61.     for (int i = 0; i < vect.size(); i++)  
  62.     {  
  63.         cout << vect[i] << endl;  
  64.     }  
  65.   
  66.     vect.resize(12, 77);                 // 此處設置vect的大小爲10,不僅大於當前vect的大小10,還大於vect的當前容量10,會爲vect重新分配存儲空間  
  67.   
  68.     cout << "size4 = " << vect.size() << endl;  
  69.     cout << "capacity4 = " << vect.capacity() << endl;  
  70.   
  71.     for (int i = 0; i < vect.size(); i++)  
  72.     {  
  73.         cout << vect[i] << endl;  
  74.     }  
  75.   
  76.     return 0;  
  77. }  

上述代碼的執行結果爲:



由上面的執行結果,可以得到下面幾個結論:

1、使用resize方法時,改變了vector中元素的個數,即vector的大小,但不會改變vector的容量大小;

2、驗證了“如果resize中所指定的n小於vector中當前的元素數量,則會刪除vector中多於n的元素,使vector得大小變爲n;”,由size1、capacity1、size1_1、capacity1_1,可知,如果resize指定的大小n小於vector當前的大小時,會減小vector的大小,但無論resize中是否指定初始化值,都不會影響vector中原本已經存在的元素值;

3、當resize中所指定的n大於vector當前的大小,但是小於vector當前的容量大小時,會在vector後面插入適量的元素,使得vector的大小滿足n,如果指定了初始值,則會把新插入的元素初始化爲指定的初始值,如果沒有指定初始值,則將會把新插入的元素初始化爲默認初始值,即0;(由size2、capacity2、size3、capacity3得出)

4、當resize中所指定的n大於vector當前的大小,並且大於vector當前的容量大小時,會爲vector重新分配存儲空間,由size4和capacity4可以看出來,其中,對於capacity = 15的原因上面已經解釋過,這裏不再贅述。


這裏再補充一點:

reserve被用來爲vector設置容量大小,但是並沒有創建vector中的元素對象,必須保證vector中有通過push_back或者insert等方法插入的元素後,才能訪問vector中的元素;而resize被用來設置vector的大小,即元素個數,同時會創建元素對象,因此可以通過重載標識符[]來訪問vector中resize所指定的大小範圍內的元素,若是採用push_back或者insert方法向vector中插入元素的話,則只會在vector的尾部插入新的元素,增加vector的元素個數,擴大vector的大小


通過下述代碼進行驗證:

1、

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     vector<int> vect;  
  9.     int i = 0;  
  10.   
  11.     vect.reserve(10);  
  12.   
  13.     cout << vect.size() << endl;  
  14.     cout << vect.capacity() << endl;  
  15.   
  16.     vect[0] = 3;  
  17.   
  18.     return 0;  
  19. }  


首先,在使用reserve爲vect設置容量大小之後,通過[]來訪問 vect中的第一個元素,此時,在編譯鏈接過程中,不會有任何問題,但是在執行的過程中會報出:



該異常信息爲:vector subscript out of range,即訪問vect越界了,由此可以看出,在使用reserve時並沒有爲vector創建任何元素對象


2、

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     vector<int> vect;  
  9.     int i = 0;  
  10.   
  11.     vect.resize(6);  
  12.   
  13.     cout << "size1 = " << vect.size() << endl;  
  14.     cout << "capacity1 = " << vect.capacity() << endl;  
  15.       
  16.     vect[5] = 17;  
  17.   
  18.     for (i = 0 ; i < vect.size(); i++)  
  19.     {  
  20.         cout << vect[i] << endl;  
  21.     }  
  22.   
  23.     vect.push_back(777);  
  24.   
  25.     cout << "size1 = " << vect.size() << endl;  
  26.     cout << "capacity1 = " << vect.capacity() << endl;  
  27.   
  28.     for (i = 0 ; i < vect.size(); i++)  
  29.     {  
  30.         cout << vect[i] << endl;  
  31.     }  
  32.   
  33.     return 0;  
  34. }  

上述代碼的執行結果爲:



由上述執行結果可以看出,在使用resize爲vector指定大小之後,會創建元素對象,可以通過[]標識符來對元素對象進行訪問,同時若是採用push_back或者insert等方法來插入元素時,實際上會在vector尾部插入新的元素,從上面的size1值的改變可以證實。

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