重點介紹一下resize()擴容和reserve()兩個函數
resize()
resize()擴容的默認構造的方式是0, 之后插入按照1 2 4 8 16 二倍擴容。注(GCC是二倍擴容,VS13是1.5倍擴容。原因可以考慮內存碎片和伙伴系統,內存的浪費)。
擴容后是一片新的內存,需要把舊內存空間中的所有元素都拷貝進新內存空間中去,之后再在新內存空間中的原數據的后面繼續進行插入構造新元素,
並且同時釋放舊內存空間,並且,由於vector 空間的重新配置,導致舊vector的所有迭代器都失效了。
reserve():
1、reserve只是保證vector的空間大小(_capacity)最少達到它的參數所指定的大小n。在區間[0, n)范圍內,預留了內存但是並未初始化
2、只有當所申請的容量大於vector的當前容量capacity時才會重新為vector分配存儲空間;小於當前容量則沒有影響
3、reserve方法對於vector元素大小沒有任何影響,不創建對象。
vector的初始的擴容方式代價太大,初始擴容效率低, 需要頻繁增長,不僅操作效率比較低,而且頻繁的向操作系統申請內存容易造成過多的內存碎片,
所以這個時候需要合理使用resize()和reserve()方法提高效率減少內存碎片的。
問題一:為什么非要以倍數的形式擴容,而不是以固定值的形式擴容。
倍數方式空間拷貝數據次數
假設vector初始的capacity=10,size=0,總共有100個元素,以2倍的形式增長。換算下來大概是需要進行5次擴容。這樣的話,相當於舊空間數據到原空間數據的拷貝有5次。
固定個數方式空間拷貝數據次數
假設vector初始的capacity=10,size=0,總共有100個元素,每次以10個元素個數的形式增長。(每次新增10個空間)。所以這次的擴容次數為 100/10 = 10次,也就是說,
插入100白個元素,需要擴容10次。
但是,如果n=1000的情況下, 以個數形式進行擴容就不能在為10了,否則拷貝空間次數將會太多
有的小伙伴要問:但是可以取100呀,想想,如果n=10的情況下,取100又不太合適,所以,以個數的形式來進行擴容顯然不符合所用n的取值。
問題二:為什么每次增長是1.5倍或者2倍形式,而不是3倍或者4倍形式增長。
如果以大於2倍的方式來進行擴容,下一次申請空間會大於之前申請所有空間的總和,這樣會導致之前的空間不能再被重復利用,這樣是很浪費空間的操作。
所以,如果擴容一般基於(1, 2] 之間進行擴容
舉個例子:
2倍擴容
1,2,4,8,16,32,...
1.5倍擴容
4,6,9,14,21,31.5,...
2倍擴容的時候,下一次擴容的內存總是比之前釋放過的內存和都大,即之前釋放過的內存在之后的擴容中不肯能被使用
1.5倍擴容的時候,以上面的例子,第6次擴容的時候,就可以重復利用之前釋放過的內存,31.5<4+6+9+14
所以為了提高內存利用率,減少擴容次數,每次擴容的倍數應該在[1.5,2]之間更合適
vector實現代碼
#ifndef _MY_VECTOR_HPP_ #define _MY_VECTOR_HPP_ template<typename T> class MyVector { public: // 構造函數 MyVector() { //這里默認數組大小為10 //但是vector文件中默認構造的方式是0, 之后插入按照1 2 4 8 16 二倍擴容。注(GCC是二倍擴容,VS13是1.5倍擴容 data = new T[10]; _capacity = 10; _size = 0; } ~MyVector() { delete[] data; } //reserve只是保證vector的空間大小(_capacity)最少達到它的參數所指定的大小n。在區間[0, n)范圍內,預留了內存但是並未初始化 void reserve(size_t n) { if(n>_capacity) { data = new T[n]; _capacity = n; } } //向數組中插入元素 void push_back(T e) { //如果 當前容量已經不夠了, 重新分配內存, 均攤復雜度O(1) if (_size == _capacity) { resize(2 * _capacity); } data[_size++] = e; } //刪除數組尾部的數據,同時動態調整數組大小,節約內存空間 T pop_back() { T temp = data[_size]; _size--; //如果 容量有多余的,釋放掉 if (_size == _capacity / 4) { resize(_capacity / 2); } return temp; } //獲取當前數組中元素的個數 size_t size() { return _size; } //判斷數組是否為空 bool empty() { return _size==0?1:0; } //重載[]操作 int &operator[](int i) { return data[i]; } //獲取數組的容量大小 size_t capacity() { return _capacity; } //清空數組,只會清空數組內的元素,不會改變數組的容量大小 void clear() { _size=0; } private: T *data; //實際存儲數據的數組 size_t _capacity; //容量 size_t _size; //實際元素個數 //擴容 void resize(int st) { //重新分配空間,在棧區新開辟內存,然后將以前數組的值賦給他,刪除以前的數組 T *newData = new T[st]; for (int i = 0; i < _size; i++) { newData[i] = data[i]; } //實際使用時是清除數據,但不會釋放內存 delete[] data; data = newData; _capacity = st; } }; #endif //_MY_VECTOR_HPP_
測試代碼
#include <iostream> #include<string> #include "MyVector.hpp" using namespace std; int main() { int size=11; MyVector<int> p; p.reserve(5); cout<<"size="<<p.size()<<"capacity="<<p.capacity()<<endl; for (int i = 0; i < size; i++) { p.push_back(i); } for(int i=0;i<size;i++) { cout<<p[i]<<' '; } cout<<endl; printf("size=%d capcity=%d\n",p.size(),p.capacity()); // MyVector<string>pp; // for(int i=0;i<5;i++) // { // pp.push_back("str"); // } // cout<<pp.size()<<" "<<pp.capacity()<<endl; cout<<endl; return 0; }