1.vector的介绍及使用
1.1 vector的介绍
vector的文档介绍
1 vector是表示可变大小数组的序列容器。
2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
1.2 vector的使用
1.2.1 vector的定义
(constructor)构造函数声明 | 接口说明 |
---|---|
vector()(重点) | 无参构造 |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (const vector& x); (重点) | 拷贝构造 |
vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
void test_vector1()
{
vector<int> v1;
vector<int> v2(10, 8);
// 下面涉及迭代器初始化的部分,我们学习完迭代器再来看这部分
vector<int> v3(++v2.begin(), --v2.end());
vector<int> v4(v3);
string s("hello world");
vector<char> v5(s.begin(), s.end());
}
1.2.2 vector iterator 的使用
iterator的使用 | 接口说明 |
---|---|
begin +end(重点) | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |
遍历访问vector
void test_vector2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
// 遍历一、operator[] 重载+遍历
for (size_t i = 0; i < v.size(); ++i)
{
v[i] += 1;
cout << v[i] << " ";
}
cout << endl;
// 遍历二、迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{
*it -= 1;
cout << *it << " ";
++it;
}
cout << endl;
// 遍历三、范围for本质就是区替换换成迭代器的代码
for (auto e : v)
{
cout << e <<" ";
}
cout << endl;
}
============================================================
int a[] = { 1, 2, 3 };
// 原生指针就是天然的迭代器,数组支持范围for,会被替换指针
for (int* p = a; p < a + sizeof(a) / sizeof(int); ++p)
{}
for (auto e : a)
{}
1.2.3 vector 空间增长问题
容量空间 | 接口说明 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize(重点) | 改变vector的size |
reserve (重点) | 改变vector放入capacity |
max_size | 可容纳的最大元素个数 |
void resize (size_type n, value_type val = value_type());
void reserve (size_type n);
void test_vector3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
cout << v.max_size() << endl;
v.reserve(100); // 扩容
v.resize(100, 5); // 扩容+初始化 或 删除数据
v.resize(2);
}
capacity增长情况
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义
的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
resize在开空间的同时还会进行初始化,影响size。
#include <iostream>
#include <vector>
int main ()
{
size_t sz;
std::vector<int> foo;
sz = foo.capacity();
std::cout << "making foo grow:\n";
for (int i=0; i<100; ++i) {
foo.push_back(i);
if (sz!=foo.capacity()) {
sz = foo.capacity();
std::cout << "capacity changed: " << sz << '\n';
return 0;
}
vs:运行结果:
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94
capacity changed: 141
g++运行结果:
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128
1.2.4 vector 增删查改
vector | 增删查改 接口说明 |
---|---|
push_back(重点) | 尾插 |
pop_back (重点) | 尾删 |
find | 查找。(注意这个是算法模块实现,不是vector的成员接口) |
insert | 在position之前插入val |
erase | 删除position位置的数据 |
swap | 交换两个vector的数据空间 |
operator[] (重点) | 像数组一样访问 |
assign
将新内容分配给vector,替换其当前内容,并相应地修改其大小。
template <class InputIterator> void assign (InputIterator first, InputIterator last);
void assign (size_type n, const value_type& val);
int main()
{
vector<int> v1(10,6);//6666666666
v1.assign(8, 5);//55555555
vector<int> v2(10, 6);//6666666666
v2.assign(v1.begin(), v1.end());//55555555
return 0;
}
insert
// 在position位置插入val
iterator insert (iterator position, const value_type& val);
// 在position位置插入n个val
void insert (iterator position, size_type n, const value_type& val);
// 在position位置插入[first,last)区间的值
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
void test1()
{
std::vector<int> v;
int arr[] = { 6,7,8,9 };
v.insert(v.end(), 1);
v.insert(v.end(), 2);
v.insert(v.end(), 3);
v.insert(v.end(), 4);
v.insert(v.end(), 2, 5);
v.insert(v.end(), arr, arr + sizeof(arr) / sizeof(arr[0]));
for (auto e : v)
std::cout << e << ' ';
std::cout << std::endl;
}
push_back
// 尾插
void push_back (const value_type& val);
在末尾添加元素
在当前最后一个元素之后,在vector 的末尾添加一个新元素。val的内容被复制(或移动)到新元素。
这有效地将容器大小增加了1,当且仅当新向量大小超过当前向量容量时,这会导致分配的存储空间的自动重新分配。
void test1()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
std::cout << e << ' ';
std::cout << std::endl;
}
erase
// 删除pos位置的元素
iterator erase (iterator position);
// 删除[first,lase) 区间的元素
iterator erase (iterator first, iterator last);
从vector中删除单个元素 ( position ) 或一系列元素 ( [first,last) )。
这通过删除的元素数量有效地减小了容器大小,这些元素位置被破坏了。
因为vector使用数组作为它们的底层存储,删除向量末端以外的位置的元素会导致容器将擦除段后的所有元素重新定位到它们的新位置。与其他类型的序列容器(例如list或forward_list)为相同操作执行的操作相比,这通常是一种低效的操作。
void test_vector4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.erase(begin());
vector<int>::iterator ret = find(v.begin(), v.end(), 3);
if (ret != v.end())
{
cout << "找到了" << endl;
v.insert(ret, 30);//这里插入可能会导致ret失效
//v.erase(ret); // 不能再这删,因为ret失效了。这个迭代器失效问题,我们后面会细讲
}
v.insert(v.begin(), -1);
}
注:迭代器失效后面会将,这里的迭代器失效是因为,vector底层是数组的,扩容时可能会改变地址。
pop_back
void pop_back();
删除vector 中的最后一个元素,有效地将容器size减小一。
#include <iostream>
#include <vector>
int main ()
{
std::vector<int> myvector;
int sum (0);
myvector.push_back (100);
myvector.push_back (200);
myvector.push_back (300);
while (!myvector.empty())
{
sum+=myvector.back();
myvector.pop_back();
}
std::cout << sum << '\n';
return 0;
}
swap
void swap (vector& x);
用 x的内容交换容器的内容,x是另一个相同类型的vector 对象。大小可能不同。
调用这个成员函数之后,这个容器中的元素就是调用前x中的元素,x中的元素就是this中的元素。所有迭代器、引用和指针对于交换的对象仍然有效。
#include <iostream>
#include <vector>
int main ()
{
std::vector<int> v1 (3,20);
std::vector<int> v2 (5,10);
foo.swap(bar);
std::cout << "v1 contains:";
for (unsigned i=0; i<v1 .size(); i++)
std::cout << ' ' << v1 [i];//10 10 10 10 10
std::cout << '\n';
std::cout << "v2 contains:";
for (unsigned i=0; i<v2 .size(); i++)
std::cout << ' ' << v2 [i];//20 20 20
std::cout << '\n';
return 0;
}
注意:vector< bool >::swap,为特化 版本。
1.2.5 vector 访问元素
value_type& operator[] (size_type n);
const value_type& operator[] (size_type n) const;
value_type& at(size_type n);
const value_type& at (size_type n) const;
value_type& front();
const value_type&front() const;
value_type& back();
const value_type&back() const;
1.2.6 vector 查找
vector没有提供成员函数find,是因为SLT中List,deque等容器也需要,所以find函数放< algorithm >头文件里。
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
函数模版只支持迭代器,
返回一个迭代器,指向范围内[first,last)与val比较的第一个元素。如果没有找到这样的元素,则函数返回last。
该函数模板的行为等价于:
template<class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val)
{
while (first!=last) {
if (*first==val) return first;
++first;
}
return last;
}
2.vector深度剖析及模拟实现
2.1 vector模拟实现
模拟实现代码:
#pragma once
#include<assert.h>
#include<iostream>
#include<string>
namespace BBQ
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
~vector()
{
if (_start)
{
delete[] _start;
_start = nullptr;
_finish = nullptr;
_endofstorge = nullptr;
}
}
vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorge(nullptr)
{}
vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _endofstorge(nullptr)
{
//……
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorge, v._endofstorge);
}
template <class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _endofstorge(nullptr)
{
while (first != last)
{
push_back(*first);
++first;
}
}
// 现代写法
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorge(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
// v2(v1)
// 传统写法
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorge(nullptr)
{
reserve(v.capacity());
for (size_t i = 0; i < v.size(); i++)
{
push_back(v[i]);
}
// 字节拷贝,当数组存储 例如string对象就不可以了
/* _start = new T[v.capacity()];
_finish = _start + v.size();
_endofstorge = _start + v.capacity();
memcpy(_start, v._start, v.size()*sizeof(T));*/
}
vector<T>& operator=( vector<T> v)
{
swap(v);
return *this;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
iterator tmp = new T[n];
// 如果有数据,就要把数据移到新空间
if (_start)
{
memcpy(tmp, _start, sizeof(T) * sz);
/*for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}*/
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorge = _start + n;
}
}
// 这里引用的匿名对象生命周期被延长
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else//n>=size
{
if (n > capacity())
{
reserve(n);
}
iterator tmp = _finish;
_finish = _start + n;
while (tmp != _finish)
{
*tmp = val;
tmp++;
}
}
}
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
// 满了就扩容
if (_finish == _endofstorge)
{
// 扩容会导致pos失效,扩容需要更新一下pos
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
void push_back(const T& val)
{
//扩容
if (_finish == _endofstorge)
{
reserve( capacity() == 0 ? 4 : capacity() * 2 );
}
*_finish = val;
_finish++;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin()const
{
return _start;
}
const_iterator end()const
{
return _finish;
}
size_t size()const
{
return _finish - _start;
}
size_t capacity()const
{
return _endofstorge - _start;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
private:
iterator _start;
iterator _finish;
iterator _endofstorge;
};
// 实现杨辉三角
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>>ret;
ret.resize(numRows);
for (int i = 0; i < numRows; i++)
{
ret[i].resize(i + 1);
// ret[i][0]=ret[i][ret[i].size()-1]=1;
int j = 0;
for (j = 0; j < ret[i].size(); j++)
{
if (j == 0 && i == 0)ret[i][j] = 1;
if (j < i && j >= 0)ret[i][j] += ret[i - 1][j];
if (j - 1 < i && j - 1 >= 0)ret[i][j] += ret[i - 1][j - 1];
}
}
return ret;
}
};
void test1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
{
std::cout << e;
}
std::cout << endl;
Solution s;
auto it=s.generate(5);
for (auto row : it)
{
for (auto col : row)
{
std::cout << col << ' ';
}
std::cout << std::endl;
}
}
// 传统写法拷贝构造测试
void test2()
{
vector<std::string> v;
std::string s1("heell1");
std::string s2("heell2");
v.push_back(s1);
v.push_back(s2);
vector<std::string> v1(v);
for (auto e : v1)
{
std::cout << e << endl;
}
std::cout << v1.size() << endl;
std::cout << v.size() << endl;
std::cout << v.capacity() << endl;
std::cout << v1.capacity() << endl;
}
// 现代写法拷贝构造测试
void test3()
{
vector<std::string> v;
std::string s1("hello 1");
std::string s2("hello 2");
v.push_back(s1);
v.push_back(s2);
vector<std::string> v1(v);//
for (auto e : v1)
{
std::cout << e << endl;
}
}
void test_vector5()
{
// 三种场景去测试
// 1 2 3 4 5 -> 正常
// 1 2 3 4 -> 崩溃
// 1 2 4 5 -> 没删除完
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(5);
// 要求删除v1所有的偶数
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
it = v1.erase(it);
}
else
{
++it;
}
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector6()
{
vector<std::string> v;
v.push_back("111111111111111111111111");
v.push_back("111111111111111111111111");
v.push_back("1111111111");
v.push_back("1111111111");
v.push_back("1111111111");
for (auto& e : v)
{
cout << e << " ";
}
cout << endl;
}
}
2.2 迭代器失效
结论:只要使用迭代器访问的容器,都可能涉及迭代器失效。
string的通常使用下标访问,所以涉及迭代器失效的场景很小。
2.2.1 insert插入扩容时迭代器失效
错误代码:
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
// 满了就扩容
if (_finish == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
void test_vector4()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::iterator it = find(v1.begin(), v1.end(), 2);
if (it != v1.end())
{
// 如果insert中发生了扩容,那么会导致it指向空间被释放
// it本质就是一个野指针,这种问题,我们就叫迭代器失效
v1.insert(it, 20);
}
// v1.insert(v1.begin(), -1);
for (auto e : v1)
{
cout << e << " ";
}
cout<<endl;
}
代码分析:如果insert中发生了扩容,
1、那么回导致pos指向的内存被释放,那么pos迭代器就是野指针迭代器失效,所以我们需要在扩容以后将POS更新一下,
2、会导致it指向空间被释放, it本质就是一个野指针,这种问题,我们就叫迭代器失效,所以说insert的返回值是迭代器,目的是为了更新it。
代码修改:
我们只需要扩容后更新pos
// 满了就扩容
if (_finish == _endofstorage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
insert参数迭代器不能是引用的原因:
iterator insert(iterator & pos, const T& x)
{
//……
}
迭代器失效,函数内容扩容后更新内存位置,导致外部迭代器失效,如果我们改用引用迭代器的方式,虽然可以外部的迭代器也一起更新了,但是有些场景下不能。
例如一下场景:
void test_vector4()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.insert(v1.begin(), -1);
for (auto e : v1)
{
cout << e << " ";
}
cout<<endl;
}
编译报错,v1.begin() 产生的临时变量,不能引用传递,引用传递必须是有实体的。
2.2.2 erase删除迭代器失效
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin-1) = *begin;
++begin;
}
--_finish;
return pos;
}
场景:要求删除v1所有的偶数
void test_vector5()
{
// 三种场景去测试
// 1 2 3 4 5 -> 正常
// 1 2 3 4 -> 崩溃
// 1 2 4 5 -> 没删除完
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(5);
// 要求删除v1所有的偶数
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
代码分析:
在Linux环境下,erase(it) 以后,it指向的位置的意义已经变了。直接++it可能会导致一些意料之外的结果,连续的偶数,导致后一个偶数没有判断,没有删掉,再其次,erase删除有些vector版本的实现,可能它会对vector进行缩容,如果是这样。erase(it)以后it也可能是野指针,根insert类似。
注意:SGI和PJ版本vector都没有这样做。
导致上述三种问题,本质都是erase(it)以后it的意义变量,再去it++就不对了。这就it失效的原因。
综合这样的情况,STL它是这样规定的,不管是什么版本的,erase返回的是删除之后的迭代器。
如图两种情况:
真确代码:
void test_vector5()
{
// 三种场景去测试
// 1 2 3 4 5 -> 正常
// 1 2 3 4 -> 崩溃
// 1 2 4 5 -> 没删除完
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(5);
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
it = v1.erase(it);
}
else
{
++it;
}
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
注意:vs对erase以后的迭代器进行了强制的检查,所以上述的错误代码三种情况都会被强制检查出来断言报错,vs这种处理方式不是很好。
2.3 慎用memcpy(深浅拷贝问题)
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
iterator tmp = new T[n];
// 如果有数据,就要把数据移到新空间
if (_start)
{
memcpy(tmp, _start, sizeof(T) * sz);
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorge = _start + n;
}
}
代码分析:
memcpy为浅拷贝,对int类型没问题,但是对string类型不行,一块资源被释放两次。
正确代码:
调用元素赋值重载即可深拷贝;
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
2.4 string的vs下的优化
//std 下的string
class string
{
private:
char _Buf[16];//字符长度小于16,就是存在这个数组中
char* _Ptr; // 大于等于16,就是去堆上申请
size_t _mySize;
size_t _myRes;
};
vs下主要利用空间换时间的方式,如果字符长度小于16就存放在栈里,不需要去堆申请内存。
在reserve为memcpy下测试:
void test_vector6()
{
vector<std::string> v;
v.push_back("111111111111111111111111");
v.push_back("111111111111111111111111");
v.push_back("1111111111");
v.push_back("1111111111");
v.push_back("1111111111");
for (auto& e : v)
{
cout << e << " ";
}
cout << endl;
}
测试结果:
因为长度小于16的字符在栈里保存,delete[]
只对堆内存释放,增容时,memcpy浅拷贝,然后对内存释放,但是在只能释放堆内存,所以长度大于16的内存被回收后呈现的效果。
2.5 迭代器命名规范
迭代器分类 | 对应容器 | 迭代器支持操作 |
---|---|---|
input_iterator、output_iterator | 没有实际对应的类型 | |
forward_iterator(单向迭代器) | forward_list、unordered_map、unordered_set | ++ |
bidirectional_iterator(双向迭代器) | list、map、set | ++、– |
randomaccess_iterator(随机迭代器) | deque、vector | ++、–、+、- |
有上往下趁继承关系,父亲能做的,儿子也能做。
例如:
sort
default (1)
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
custom (2)
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
sort函数内部需要三数取中,三数取中需要++,–,+,-等操作,所以sort函数必须传随机迭代器,而传入父类迭代器则运行出错。
再例如:
reverse
template <class BidirectionalIterator>
void reverse (BidirectionalIterator first, BidirectionalIterator last);
reverse支持传入随机迭代器和双向迭代器。
迭代器的命名规范的目的就是为了让程序员起提示作用;