目錄
1.vector的概述
2.vector常用介面
2.1 建構式
2.2 迭代器的使用:
2.3 修改的介面
push_back
pop_back
insert
erase
find
reverse
2.4 關于容量介面
resize
reverse
2.5 資料的訪問
3 vector的迭代器
迭代器失效的問題
4. vector的模擬實作
4.1reserve介面的實作
4.2 resize
4.3 insert
4.4 push_back
4.5 erase
4.6 pop_back
4.7 建構式
1.vector的概述
vector本質是資料結構中的線性表,它是動態空間的陣列,如果vector中的空間滿了以后,它會先開辟一塊更大的新空間,然后將舊空間的資料拷貝給新空間,再把舊空間的給釋放掉,vector開辟的新空間的容量一般是舊空間的容量的兩倍或者1.5倍,不同的編譯器增容方式可能是不同的,例如再g++中vector是以兩倍增長的,vs中vector是以1.5倍增長的,
vector可以儲存任意型別的資料,包括自定義型別,當定義vector的時候,需要指明vector存盤的資料是什么型別的,定義格式:
vector<型別> 名字;
std::vector<int> v1;//v1是存盤int型別的資料
std::vector<string> v2;//v2存盤的是string型別的資料
std::vector<char> v3;//v3是存盤char型別的資料
2.vector常用介面
vector的介面有很多,在這里我就不一 一講解,我只講一些我們經常會用到的介面,如果遇到哪個介面不懂的話,自己可以去查一下檔案:vector - C++ Reference
2.1 建構式
| vector | 無參的建構式 |
|
vector
(
size_type n, const value_type& val = value_type()
)
| 構造并初始n個val |
|
vector (const vector& x);
| 拷貝構造 |
|
vector (InputIterator fifirst, InputIterator last);
| 利用迭代器進行初始化構造 |
std::vector<int> v1;//無參的建構式
std::vector<int> v2(10, 2);//構造并初始化10個2
std::vector<int> v3(v2.begin(), v2.end());//利用v2的迭代器初始化v3
vector<int> v4(v2);//拷貝構造
2.2 迭代器的使用:
正向迭代器:begin+end 回傳的的型別是iterator/const_iterator

反向迭代器:rbegin+rend 回傳的型別是reverse_iterator/const_reverse_iterator

利用迭代器遍歷vector v1,v1中包含元素有 1 ,2 ,3,4,5
void test(vector<int> v)
{
vector<int>::iterator it = v.begin();
cout << "正向迭代器遍歷:";
while (it != v.end())//正向迭代器遍歷
{
cout << *it << ' ';
it++;
}
cout << endl;
vector<int>::reverse_iterator rt = v.rbegin();
cout << "反向迭代器遍歷:";
while (rt != v.rend())//反向迭代器遍歷
{
cout << *rt << ' ';
rt++;
}
cout << endl;
}
輸出:
正向迭代器遍歷:1 2 3 4 5
反向迭代器遍歷:5 4 3 2 1
2.3 修改的介面
| push_back | 在末尾插入資料 |
| pop_back | 洗掉末尾的資料 |
| insert | 在position之前插入val |
| erase | 洗掉position位置的資料 |
| find | 查找的某個資料的位置 |
| operator[ ] | 像陣列一樣去訪問vector |
| swap | 與另一個vector進行交換 |
| reverse | 將vector中的資料進行逆置 |
push_back
void push_back (const value_type& val);
push_back介面的引數只有一個,它可以在vector的末尾插上一個資料,
pop_back
void pop_back();
pop_back介面沒有引數,它直接洗掉vector末尾的一個資料,
insert
insert有3種傳參的方式,
方式一:
iterator insert (iterator position, const value_type& val);
在position之前插入一個val,position是迭代器型別
方式二:
void insert (iterator position, size_type n, const value_type& val);
在position之前插入n個val,positon是迭代器型別
方式三:
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
position 和first,last都是迭代器型別,
將一個vector的[first,last)區間的資料插入到另一個相同型別的vector的position位置之前,
?
pisition不會隨著資料的移動而移動,
erase
erase有兩種傳參的方式:
iterator erase (iterator position);
方式一:洗掉position位置的資料、
iterator erase (iterator first, iterator last);
方式二:洗掉迭代器區間 [ first , lasr ) 中的元素,

find
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
在[ first , last )區間查找val,找到了就回傳相應的迭代器,找不到就回傳last,
find不是vector中的中的介面,是演算法中的函式,只要有迭代器都可以通過find進行查找,
reverse
template <class BidirectionalIterator>
void reverse (BidirectionalIterator first, BidirectionalIterator last);
傳vector的迭代區間,將vector的迭代區間中的元素進行逆置,例如:將一個vector中1 2 3 4 5 進行逆置就成 5 4 3 2 1
std::vector<int> v1;//無參的建構式
v1.push_back(1);//尾插
v1.push_back(2);//尾插
v1.push_back(3);//尾插
v1.push_back(4);//尾插
v1.push_back(5);//尾插
//查找3的位置,并將回傳的迭代器存盤在pos
vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);
v1.insert(pos, 30);//在pos位置之前插入30
reverse(v1.begin(), v1.end());//逆置vector
//查找3的位置,并將回傳的迭代器存盤在it
vector<int>::iterator it = find(v1.begin(), v1.end(), 4);
v1.erase(it);//洗掉it位置的資料
2.4 關于容量介面
| size | 獲取資料個數 |
| capacity | 獲取容量的大小 |
| empty | 判斷vector是否為空 |
| reserve | 改變vector的capacity |
| resize | 改變vector的size |
resize
void resize (size_type n, value_type val = value_type());使size到n個,如果不足本身的size不足n,則用val補齊vector到n個資料,如果本身的size大于n,則不需要進行任何操作,
reverse
void reserve (size_type n);
使vector有n個容量空間,如果原本的capacity大于n,則不會縮容,
std::vector<int> v;
cout << v.capacity() << endl;//輸出0
cout << v.size() << endl;//輸出0
v.reserve(10);//預留10個capacity的空間大小
cout << v.capacity() << endl;//輸出10
cout << v.size() << endl;//輸出0
v.resize(20,10);//預留20個資料
cout << v.capacity() << endl;// 輸出20
cout << v.size() << endl;//輸出20
2.5 資料的訪問
| operator[ ] | 可以像陣列下標去訪問vector中的元素 |
| front | 回傳vector中的第一個資料 |
| back | 返滬vector中最后一個資料 |
3 vector的迭代器
vector是一個連續的空間,它的迭代器就是原生指標,因為vector中的原生指標可以滿足迭代器 operator*,operator[ ],operator++,operator--,operator+=,operator-=的操作,所以vector的迭代器就只需要給原生指標起個別名就夠了,如下:
template<class T>
class vector
{
public:
typedef T* iterator; //vector的迭代器就是原生指標
typedef const T* const_iterator;...
}
迭代器失效的問題
在我們用vector中,經常會碰到迭代器失效的問題,那么什么是迭代器失效呢?
我們先來看一段代碼,如下:
void test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(10);
v.push_back(4);
vector<int>::iterator pos = find(v.begin(), v.end(), 2);
v.insert(pos,10);
v.insert(pos, 20);
}
這段代碼在vs運行下會奔潰掉,為什么呢?

當我們洗掉掉2后,pos指向的位置的值就變為10,那么我們pos的意義也就改變了,此時的pos就迭代器就已經失效了,當我們再去使用它的時候,vs編譯器就檢查出來,
那么我再來看一下下面這個場景:

上面pos已經是野指標,因為舊空間被釋放掉所以不知道它指向的是什么值,所以該指標舊已經失效了,
所以迭代器失效有兩種方式:
一種是使用insert,erase使迭代器指向的數值發生改變,此時的迭代器就失去意義,這種有
些編譯器會檢查例如vs,有些編譯器不會檢查,例如g++
另一種是插入資料的時候,容量滿的時候會開辟新空間,舊空間被釋放,導致pos指向的資料
不知道是什么,此時的pos成了野指標,這個任何編譯器都會報錯,
4. vector的模擬實作
namespace sjp
{
template<class T>
class vector
{
public:
typedef T* iterator; //vector的迭代器是可以修改的
typedef const T* const_iterator;
iterator begin()//回傳vector迭代區間的頭
{
return _first;
}
iterator end()//回傳vector的迭代區間的尾
{
return _end;
}
const_iterator begin()const
{
return _first;
}
const_iterator end()const
{
return _end;
}
size_t size()const//回傳vector的資料個數
{
return _end - _first;
}
size_t capacity()const//回傳vector的容量
{
return _end_of_storage - _first;
}
bool empty()const//判斷vector是否為空
{
return _first == _end;
}
const T& operator[](size_t i)const
{
return *(_first + i);
}
T& operator[](size_t i)
{
return *(_first + i);
}
void clear()//清空vector
{
_end = _first;
}
const T& back()const//回傳vector最后一個元素
{
return *(back - 1);
}
const T& front()const//回傳vector第一個元素
{
return *(front);
}
private:
iterator _first;//表示目前使用空間的頭
iterator _end;//表示目前使用空間的尾
iterator _end_of_storage;//表示目前可用空間的尾
};
在上面介面中,我們發現
1.有些介面在后面需要加上const,例如 empty ,size, capacity這些介面,因為呼叫這些介面的時候不會修改它的成員變數,所以我們加上const可以防止它的成員變數被修改,并且可以讓const物件和非const物件都可以呼叫這些介面(注意非const物件是可以呼叫const的介面,但const物件不能呼叫非const的介面)
2.有些介面需要實作const和非const的版本,例如begin ,end,operator[]這些介面,
非const物件呼叫非const的介面,回傳的物件是可讀可寫的,const物件呼叫的是const的介面
回傳的物件是只能讀,例如,const物件回傳的也是const的迭代器,就是不能通過迭代器去修改它指向的值,
4.1reserve介面的實作

void reserve(size_t n)
{
size_t sz = size();//先保存原本的size
if (capacity() < n)
{
T* tmp = new T[n];//重新開一塊空間
if (_first)//如果_first不為空指標
{
for (int i = 0; i < size(); i++)//將原空間的值拷貝給新空間
{
tmp[i] = _first[i];
}
delete[] _first;//釋放舊空間
}
//讓迭代器指向新空間
_first = tmp;
_end = _first + sz;
_end_of_storage = _first + n;
}
}
這里有一個問題,上面的原空間的值拷貝給新空間中的:
for (int i = 0; i < size(); i++)
{
tmp[i] = _first[i];
}
可不可用memcpy(tmp, _first, sz*sizeof(T));來替代
memcpy進行的淺拷貝,當vector存盤的像string開辟動態空間的之類就會出錯

也就是說memcpy只能拷貝string中的指標,沒有將拷貝string的存盤字串的空間,當舊空間的中的存盤的指標被釋放掉后,同時指標指向的子符串也會被釋放掉,
那這段代碼為什么可以呢?
for (int i = 0; i < size(); i++)
{
tmp[i] = _first[i];
}

4.2 resize
void resize(size_t n,T val=T())
{
if (n > size())
{
if (n > capacity())//如果預留資料個數大于容量則需要增容
{
reserve(n);
}
size_t sz = n - size();//將資料補齊到n個
for (int i = 0; i < sz; i++)
{
push_back(val);
}
}
}
如果n大于size的時候就插入val,再進行判斷,如果資料大于capacity的時候就需要進行增容,最后將val值補齊使vector的資料個數為n個,
4.3 insert
void insert(iterator pos,const T& x)
{
assert(pos >= _first && pos <= _end);
if (_end == _end_of_storage)//判斷容量是否滿了
{
size_t lenth = pos - _first;//如果換新空間,需要保留pos在vector的位置
int newcapacity = capacity() == 0 ? 5 : capacity() * 2;
reserve(newcapacity);//預留新空間
pos =_first+lenth;//跟新pos
}
//將x插入到pos位置的前面
iterator cur = end();
while (cur != pos)
{
*(cur) = *(cur-1);
cur--;
}
*pos = x;
_end += 1;//資料個數再加一
}

上面提到了insert迭代器會失效的問題,那么我們可不可以對insert適當修改一下使我們使用insert
不會發生迭代器失效的問題?
只需要在上面的代碼的基礎,在pos迭代器加上參考&,然后插入資料后,pos加1,這樣插入資料后,pos就可以一直指向原本那個資料,

4.4 push_back
void push_back(const T& x)//尾插,直接在最后一個位置插入資料即可
{
insert(_end, x);
//if (_end == _end_of_storage)
//{
// int newcapacity = capacity() == 0 ? 5 : capacity() * 2;
// reserve(newcapacity);
//}
//*_end = x;
//++_end;
}
4.5 erase
iterator erase(iterator pos)
{
assert(pos < _end);
iterator cur = pos;//將pos位置后的所有資料向前挪一步
while (cur != _end - 1)
{
*cur = *(cur + 1);
cur++;
}
_end--;//資料個數再減一
return pos;
}

4.6 pop_back
void pop_back()
{
--_end;
}
4.7 建構式
vector(size_t n, T val)//構造n個val值的vector
{
reserve(n);//先預留n個資料的空間
for (int i = 0; i < n; i++)//再將val存滿這塊空間
{
push_back(val);
}
}
vector()//無參的拷貝構造
:_first(nullptr),_end(nullptr),_end_of_storage(nullptr)
{
}
vector(iterator first, iterator end)//利用迭代器區間構造vector
:_first(nullptr), _end(nullptr), _end_of_storage(nullptr)
{
while (first != end)
{
push_back(*first);
first++;
}
}
//v2(v1);
vector(const vector<T>& v)//拷貝建構式
:_first(nullptr),_end(nullptr),_end_of_storage(nullptr)
{
reserve(v.capacity());//預留v1相同的空間
for ( auto e: v)//然后將v1中的資料一個一個的推進v2
{
push_back(e);
}
}
//v2=v1
vector<T>& operator=(const vector<T>& v)//賦值建構式
{
vector<T> tmp(v);
swap(_first, tmp._first);//不能用用=賦值進行賦值,tmp出函式作業域或被析構掉,
swap(_end, tmp._end);
swap(_end_of_storage, tmp._end_of_storage);
return *this;
}
v1=v2,將v1賦值給v2,需要將v1的空間大小和值跟v2是一模一樣的,在賦值程序中,先拷貝構造一個跟v1一模一樣的tmp,然后將tmp和v2中_first,_end, _end_of_storage交換過去,出函式作用域的時候,tmp會被解構式給釋放掉,此時v1的空間大小和值跟v2是一模一樣的,
總結:在上面中的push_back,pop_back,insert,erase,clear等介面中在后面不需要加上const,因為const物件本身不能被修改的,const的vector中的資料個數和資料都不能被改變,所以說const物件不能呼叫這些介面,
好了,今天的知識就分享到這里,喜歡的小伙伴們可以給博主點個贊 ,或者點個關注,大家一起努力學習,共同進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/302281.html
標籤:其他

如果n大于size的時候就插入val,再進行判斷,如果資料大于capacity的時候就需要進行增容,最后將val值補齊使vector的資料個數為n個,