技術在於交流、溝通,本文為博主原創文章轉載請注明出處並保持作品的完整性。
向量容器vector是一個動態數組,內存連續,它是動態分配內存,且每次擴張的原來的二倍.
他的結構如下
一 定義
vector< 類型 > 標識符(最大容量,初始所有值)
vector是一種類模板,那么他有很多行為與類相似
頭文件 #include <vector>
//a.定義 vector<typeName> v; vector<int> v; //b.拷貝構造 vector<typeName> v1(v); 例:vector<int> v1(v); vector<int> v1(v); //c.賦值拷貝 v1 = v; //如果v的size比v1的size大,則自動擴充v1的空間,反之亦然 //d.按指定元素個數定義 vector<int> v2(5); //v2含有5個值為0的元素 //e.指定元素個數及類型 vector<int> v3(5,10);//v3包含5個值為10的int類型元素 //f.與array間的轉換 int a[3]={0,1,2}; vector<int> v4(a,a+3);
二 基本使用
vector<int> v; //在vector尾端插入元素 //但是沒有在前面插入元素,上面的vector結構圖中,我們可以看出來vector是一種向后擴充的容器, //如果在前面插入,那后面所有的元素將后移,造成巨大的消耗,所以沒有push_front() //同理沒有pop_front() v.push_back(1); //刪除最后一個元素 v.pop_back(); //返回元素個數 int count = v.size(); //重新設定vector的size v.resize(2*(v.size())); //判斷容器是否為空 bool isEmpty = v.empty(); //[index]操作,返回下表為index的元素 int tmp = v[1]; //定義迭代器 vector<int> ::iterator iter = v.begin(); for(int i = 0; i<3; i++ ) { v.push_back(1); // 1 1 1 } //在 v的前面插入兩個 5 v.insert(iter, 2, 5); // 5 5 1 1 1 //在頭部插入3 v.insert(v.begin(), 3);//3 5 5 1 1 1 //在尾部插入3 v.insert(v.end(), 3);//3 5 5 1 1 1 3 //下表5的前面插入3 v.insert(v.begin()+5, 3);//3 5 5 1 1 3 1 3 //刪除指定下標元素 v.erase(v.begin()+1); //3 5 1 1 3 1 3 //清空 v.clear(); //起始地址 v.data(); //最后一個元素后面的地址 v.end(); //實際內存大小 v.capacity(); //at(下標) v.at(1); //返回最后一個元素 v.back(); //返回第一個元素 v.front(); //將指定區間內的元素賦值給v v.assign(v.begin()+1, v.begin()+2); //賦值 將三個 1 賦值給v 那么vecotr將變為 1 1 1 v.assign(3, 1); //最大內存 v.max_size(); //輸出 for(auto iii : v) { cout << iii <<endl; }
三 vector支持的算法
增加頭文件#include<algorithm> //算法
#include <algorithm> int main() { //可以使用的全局算法有 //搜索算法:find() 、search() 、count() 、find_if() 、search_if() 、count_if() //分類排序:sort() 、merge() //刪除算法:unique() 、remove() //生成和變異:generate() 、fill() 、transformation() 、copy() //關系算法:equal() 、min() 、max() vector<int> c; for(int i = 0; i<10; i++ ) { c.push_back(i); // } //查找函數 find(begin,end, searchItem) auto pItem = ::find(c.begin(), c.end(), 3); if(pItem != c.end()) cout << "找到了: " << *pItem << endl; else cout << "沒找到" << endl; vector<int> c1; for(int i = 0; i<10; i++ ) { c1.push_back(i+10); // } //查找函數search 是否包含子序列容器向量 如果包含 返回包含位置 auto pItem1 = ::search(c.begin(), c.end(), c1.begin()+2, c1.begin()+5); if(pItem1 != c.end()) cout << "找到了: " << *pItem1 << endl; else cout << "沒找到" << endl; //算法就不一一舉例了 return 0; }
輸出結果
四 內存管理
上面提到過vector的擴充是以2倍的形式擴充,它的擴充過程可以理解成 if (v.size()元素個數 > v.capacity()實際內存) v.resize(2*v.capacity())
當vector發現元素個數大於實際內存時, vector將重新申請一塊內存為原來的內存2倍的空間 然后將原來的元素一一copy過去,我們都知識申請內存是非常耗時的,所以我們一定要把握好vector的內存尺度
下面來測試一下
void vector_test_capactity() { //創建vector std::vector<int> v; for(int i = 0; i<10 ; i++) { cout<<"容器內元素個數: " << v.size() << " "<<"vector內存大小: " << v.capacity()<<endl; v.push_back(i); } }
輸出結果
Source Code
下圖是vector類圖,類圖中的函數為vector一些常用函數,現在來分析一下這些函數
下面是我在Qt中找出的vector的源代碼
_Vector_base<_Tp>與_Vector_impl<_Tp>
1 template<typename _Tp, typename _Alloc> 2 struct _Vector_base 3 { 4 typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template 5 rebind<_Tp>::other _Tp_alloc_type; 6 typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer 7 pointer; 8 9 struct _Vector_impl 10 : public _Tp_alloc_type 11 { 12 pointer _M_start; 13 pointer _M_finish; 14 pointer _M_end_of_storage; 15 ... 16 } 17 18 pointer 19 _M_allocate(size_t __n)//申請內存 20 { 21 typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr; 22 return __n != 0 ? _Tr::allocate(_M_impl, __n) : 0; 23 } 24 25 void 26 _M_deallocate(pointer __p, size_t __n)//釋放內存 27 { 28 typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr; 29 if (__p) 30 _Tr::deallocate(_M_impl, __p, __n); 31 } 32 }
你會發現_Vector_base中has a(組合)一個_Vector_impl類,這個類里面包含三個指針分別是_M_start,_M_finish,_M_end_of_storage,結合一下下圖你就會明白這三個指針的含義
圖中的start和finish,end_of_storage分別是上面的三個指針
這個時候我們看下面的源代碼begin(),end(),capacity()是不是很容易理解了,_GLIBCXX_NOEXCEPT是不拋出任何異常
iterator begin() _GLIBCXX_NOEXCEPT { return iterator(this->_M_impl._M_start); }//返回它的start指針 ... const_iterator end() const _GLIBCXX_NOEXCEPT { return const_iterator(this->_M_impl._M_finish); }//返回finish指針 ... size_type capacity() const _GLIBCXX_NOEXCEPT { return size_type(this->_M_impl._M_end_of_storage//返回start-finish - this->_M_impl._M_start); }
下面我們來看一看vector<_Tp>類,幾個相對常用的函數push_back(), operator=和insert()
template<typename _Tp, typename _Alloc = std::allocator<_Tp> > class vector : protected _Vector_base<_Tp, _Alloc> { ... void push_back(const value_type& __x)() ... vector& operator=(const vector& __x); ... iterator insert(const_iterator __position, size_type __n, const value_type& __x) }
1.push_back()
void push_back(const value_type& __x) { if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)//如果不需要申請內存 { _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,__x);//將x賦給finish ++this->_M_impl._M_finish; // => ++finish } else #if __cplusplus >= 201103L _M_emplace_back_aux(__x); #else _M_insert_aux(end(), __x); #endif }
_M_emplace_back_aux
#if __cplusplus >= 201103L template<typename _Tp, typename _Alloc> template<typename... _Args> void vector<_Tp, _Alloc>:: _M_emplace_back_aux(_Args&&... __args) { const size_type __len = _M_check_len(size_type(1), "vector::_M_emplace_back_aux"); pointer __new_start(this->_M_allocate(__len));//申請一段新的內存,將新的start指針指向新內存的起始位置 pointer __new_finish(__new_start);//初始化finish指針 __try { _Alloc_traits::construct(this->_M_impl, __new_start + size(), std::forward<_Args>(__args)...); __new_finish = 0; __new_finish = std::__uninitialized_move_if_noexcept_a (this->_M_impl._M_start, this->_M_impl._M_finish, __new_start, _M_get_Tp_allocator());//將finish指針指向新內存的尾端 ++__new_finish; } __catch(...) { if (!__new_finish)//申請不成功銷毀 _Alloc_traits::destroy(this->_M_impl, __new_start + size()); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator());//銷毀舊內存 _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; }
2.operator=,運用assign函數重新賦值vector
vector& operator=(initializer_list<value_type> __l) { this->assign(__l.begin(), __l.end()); return *this; }
3.insert(),參數,插入坐標,內存大小,插入變量
iterator insert(const_iterator __position, size_type __n, const value_type& __x) { difference_type __offset = __position - cbegin();//獲取間隔 _M_fill_insert(begin() + __offset, __n, __x);//插入變量 return begin() + __offset;//返回插入位置 }
參考<<侯捷STL標准庫>>