3.2 vector容器
3.2.1 vector容器基本概念
vector的數據安排以及操作方式,與array非常相似,兩者的唯一差別在於空間的運用的靈活性。Array是靜態空間,一旦配置了就不能改變,要換大一點或者小一點的空間,可以,一切瑣碎得由自己來,首先配置一塊新的空間,然後將舊空間的數據搬往新空間,再釋放原來的空間。Vector是動態空間,隨着元素的加入,它的內部機制會自動擴充空間以容納新元素。因此vector的運用對於內存的合理利用與運用的靈活性有很大的幫助,我們再也不必害怕空間不足而一開始就要求一個大塊頭的array了。
Vector的實現技術,關鍵在於其對大小的控制以及重新配置時的數據移動效率,一旦vector舊空間滿了,如果客戶每新增一個元素,vector內部只是擴充一個元素的空間,實爲不智,因爲所謂的擴充空間(不論多大),一如剛所說,是”配置新空間-數據移動-釋放舊空間”的大工程,時間成本很高,應該加入某種未雨綢繆的考慮,稍後我們便可以看到vector的空間配置策略。
3.2.2 vector迭代器
Vector維護一個線性空間,所以不論元素的型別如何,普通指針都可以作爲vector的迭代器,因爲vector迭代器所需要的操作行爲,如operaroe*, operator->, operator++, operator--, operator+, operator-, operator+=, operator-=, 普通指針天生具備。Vector支持隨機存取,而普通指針正有着這樣的能力。所以vector提供的是隨機訪問迭代器(Random Access Iterators).
根據上述描述,如果我們寫如下的代碼:
Vector<int>::iterator it1;
Vector<Teacher>::iterator it2;
it1的型別其實就是Int*,it2的型別其實就是Teacher*.
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vector> using namespace std;
int main(){
vector<int> v; for (int i = 0; i < 10;i ++){ v.push_back(i); cout << v.capacity() << endl; // v.capacity()容器的容量 }
system("pause"); return EXIT_SUCCESS; } |
3.2.3 vector的數據結構
Vector所採用的數據結構非常簡單,線性連續空間,它以兩個迭代器_Myfirst和_Mylast分別指向配置得來的連續空間中目前已被使用的範圍,並以迭代器_Myend指向整塊連續內存空間的尾端。
爲了降低空間配置時的速度成本,vector實際配置的大小可能比客戶端需求大一些,以備將來可能的擴充,這邊是容量的概念。換句話說,一個vector的容量永遠大於或等於其大小,一旦容量等於大小,便是滿載,下次再有新增元素,整個vector容器就得另覓居所。
注意:
所謂動態增加大小,並不是在原空間之後續接新空間(因爲無法保證原空間之後尚有可配置的空間),而是一塊更大的內存空間,然後將原數據拷貝新空間,並釋放原空間。因此,對vector的任何操作,一旦引起空間的重新配置,指向原vector的所有迭代器就都失效了。這是程序員容易犯的一個錯誤,務必小心。 |
3.2.4 vector常用API操作
3.2.4.1 vector構造函數
vector<T> v; //採用模板實現類實現,默認構造函數 vector(v.begin(), v.end());//將v[begin(), end())區間中的元素拷貝給本身。 vector(n, elem);//構造函數將n個elem拷貝給本身。 vector(const vector &vec);//拷貝構造函數。
//例子 使用第二個構造函數 我們可以... int arr[] = {2,3,4,1,9}; vector<int> v1(arr, arr + sizeof(arr) / sizeof(int)); |
3.2.4.2 vector常用賦值操作
assign(beg, end);//將[beg, end)區間中的數據拷貝賦值給本身。 assign(n, elem);//將n個elem拷貝賦值給本身。 vector& operator=(const vector &vec);//重載等號操作符 swap(vec);// 將vec與本身的元素互換。 |
3.2.4.3 vector大小操作
size();//返回容器中元素的個數 empty();//判斷容器是否爲空 resize(int num);//重新指定容器的長度爲num,若容器變長,則以默認值填充新位置。如果容器變短,則末尾超出容器長度的元素被刪除。 resize(int num, elem);//重新指定容器的長度爲num,若容器變長,則以elem值填充新位置。如果容器變短,則末尾超出容器長>度的元素被刪除。 capacity();//容器的容量 reserve(int len);//容器預留len個元素長度,預留位置不初始化,元素不可訪問。 |
3.2.4.4 vector數據存取操作
at(int idx); //返回索引idx所指的數據,如果idx越界,拋出out_of_range異常。 operator[];//返回索引idx所指的數據,越界時,運行直接報錯 front();//返回容器中第一個數據元素 back();//返回容器中最後一個數據元素 |
3.2.4.5 vector插入和刪除操作
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count個元素ele. push_back(ele); //尾部插入元素ele pop_back();//刪除最後一個元素 erase(const_iterator start, const_iterator end);//刪除迭代器從start到end之間的元素 erase(const_iterator pos);//刪除迭代器指向的元素 clear();//刪除容器中所有元素 |
3.2.5 vector小案例
3.2.5.1巧用swap,收縮內存空間
3.2.5.2 reserve預留空間
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vector> using namespace std;
int main(){
vector<int> v;
//預先開闢空間 v.reserve(100000);
int* pStart = NULL; int count = 0; for (int i = 0; i < 100000;i ++){ v.push_back(i); if (pStart != &v[0]){ pStart = &v[0]; count++; } }
cout << "count:" << count << endl;
system("pause"); return EXIT_SUCCESS; } |
03 vector 容器 單端數組,動態數組
構造 賦值
大小 size 重置大小 resize 容量 capacity
是否爲空empty 交換數據 swap 兩個容器之間進行
巧用swap收縮空間
reserve預留空間
insert插入 erase刪除(迭代器) clear()清空
pop_back() 尾刪 front()第一個數據 back()最後一個數據
逆序遍歷 recerse_iterator rbegin rend
如何區分迭代器是否支持隨機訪問:
在迭代器上加一個值,不報錯就支持隨機訪問,否則不支持
#include <iostream>
#include <vector>
#include <list>
using namespace std;
void test01(){
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
cout << v.capacity() << endl;
//v.capacity()容器的容量
//動態數組按2倍方式擴容
//vector內部有自己的算法 別人問 不知道就可以,內部有一套策略
}
/*
所謂動態增加大小,並不是在原空間之後續接新空間(因爲無法保證原
空間之後尚有可配置的空間),而是一塊更大的內存空間,
然後將原數據拷貝新空間,並釋放原空間。因此,
對vector的任何操作,一旦引起空間的重新配置,
指向原vector的所有迭代器就都失效了。
這是程序員容易犯的一個錯誤,務必小心。
*/
}
/*
1 vector構造函數
vector<T> v; //採用模板實現類實現,默認構造函數
vector(v.begin(), v.end());//將v[begin(), end())區間中的元素拷貝給本身。
vector(n, elem);//構造函數將n個elem拷貝給本身。
vector(const vector &vec);//拷貝構造函數。
//例子 使用第二個構造函數 我們可以...
int arr[] = {2,3,4,1,9};
vector<int> v1(arr, arr + sizeof(arr) / sizeof(int));
2 vector常用賦值操作
assign(beg, end);//將[beg, end)區間中的數據拷貝賦值給本身。
assign(n, elem);//將n個elem拷貝賦值給本身。
vector& operator=(const vector &vec);//重載等號操作符
swap(vec);// 將vec與本身的元素互換。
3 vector大小操作
size();//返回容器中元素的個數
empty();//判斷容器是否爲空
resize(int num);//重新指定容器的長度爲num,若容器變長,則以默認值填充新位置。如果容器變短,則末尾超出容器長度的元素被刪除。
resize(int num, elem);//重新指定容器的長度爲num,若容器變長,則以elem值填充新位置。如果容器變短,則末尾超出容器長>度的元素被刪除。
capacity();//容器的容量
reserve(int len);//容器預留len個元素長度,預留位置不初始化,元素不可訪問。
*/
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test02() {
vector<int> v;
int arr[] = { 2,3,4,1,9 };
vector<int> v1(arr, arr + sizeof(arr) / sizeof(int));//數組名相當於begin 後邊那個相當於end
vector<int>v2(v1.begin(), v1.end());
printVector(v2);
vector<int>v3(10, 100);//10個100
printVector(v3);
//賦值使用
vector<int>v4;
v4.assign(v3.begin(), v3.end());
printVector(v4);
v4.swap(v2);
cout << "交換後的v2" << endl;
printVector(v4);
//size()統計元素個數
cout << "v4容器的大小: " << v4.size() << endl;//5
if (v4.empty()) {
cout << "v4空" << endl;
}
else {
cout << "v4不空!" << endl;
}
//if(v4.size()==0) 也是空的
//v4 2 3 4 1 9
v4.resize(7);//重新制定大小爲10
printVector(v4);//默認補充0,填充
v4.resize(10, -1);//第二個參數是默認值,默認是0,否則根據用戶的條件填充
printVector(v4);
v4.resize(3);//刪除後續的
printVector(v4);
//reserve提前開闢好空間 元素不可訪問
}
//小技巧
void test03() {
vector<int> v;
for (int i = 0; i < 100000; i++) {
v.push_back(i);
}
cout << "v的容量" << v.capacity() << endl;//大於100000
cout << "v的大小" << v.size() << endl;//100000
v.resize(3);
cout << "v的容量" << v.capacity() << endl;//大於100000
cout << "v的大小" << v.size() << endl;//3
//巧用swap()
vector<int>(v).swap(v);
cout << "v的容量" << v.capacity() << endl;//大於100000
cout << "v的大小" << v.size() << endl;//3
//vector<int>(v) 利用v初始化匿名對象 swap交換指針
//忽然想起 Qt中的qDebug()也是匿名對象,自動回收
}
//reserve(int len);//容器預留len個元素長度,預留位置不初始化,元素不可訪問。
void test04() {
vector<int> v;
int* p = NULL;
int num = 0;
v.reserve(100000);
//知道數據量是多少,預留出空間,提高效率,不預留 開闢30次空間
for (int i = 0; i < 100000; i++) {
v.push_back(i);
if (p != &v[0]) {
p = &v[0];
num++;
}
}
cout << num << endl;
//開闢100000個數據用來多少次,開闢了多少次空間
}
/*
4 vector數據存取操作
at(int idx); //返回索引idx所指的數據,如果idx越界,拋出out_of_range異常。
operator[];//返回索引idx所指的數據,越界時,運行直接報錯
front();//返回容器中第一個數據元素
back();//返回容器中最後一個數據元素
5 vector插入和刪除操作
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count個元素ele.
push_back(ele); //尾部插入元素ele
pop_back();//刪除最後一個元素
erase(const_iterator start, const_iterator end);//刪除迭代器從start到end之間的元素
erase(const_iterator pos);//刪除迭代器指向的元素
clear();//刪除容器中所有元素
*/
void test05() {
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
cout << "v的front " << v.front() << endl;
cout << "v的back " << v.back() << endl;
//找極值 排序,取收尾的操作很方便
//插入操作
v.insert(v.begin(), 2,100);//參數一是迭代器,參數二是 有幾個插入的數 參數3是具體插入的內容
printVector(v);
//刪除操作
v.pop_back();//刪了一個元素(尾刪)
printVector(v);
v.erase(v.begin());
printVector(v);
//刪除所有元素
//v.erase(v.begin(), v.end());
v.clear();//清空所有的數據
if (v.empty()) {
cout << "爲空" << endl;
}
}
void test06() {
//逆序遍歷
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
printVector(v);
//逆序的迭代器
for (vector<int>::reverse_iterator it = v.rbegin(); it != v.rend(); it++) {
cout << *it << " ";
}
cout << endl;
//vector 迭代器是隨機訪問的迭代器,支持跳躍式訪問
vector<int>::iterator itBegin = v.begin();
itBegin =itBegin+3;
//如果上述寫法不報錯,這個迭代器可以支持隨機訪問迭代器
list<int>l;
for (int i = 0; i < 10; i++) {
l.push_back(i);
}
//list<int>::iterator lIt = l.begin();
//lIt = lIt + 1;//報錯,不支持隨機訪問
}
int main() {
//test01();
//test02();
//test03();
//test04();
//test05();
test06();
return 0;
}
(本筆記內容整理自網絡資源,侵刪)