目录
Vector 是 C++ STL 中最常用的容器之一,而使用STL的三个境界:第一境界是熟用,第二境界是了解泛型技术的内涵与STL的学理乃至实作,第三境界是能扩展STL。那么接下来我们学习vector也需要按照这种方法学习。
一.vector的使用
vector的构造函数
vector的构造函数有很多,但我们只要会我们常用的就可以了。
| 构造函数声明 | 接口说明 |
| vector()(重点) | 无参构造 |
| vector(size_type n, const value_type& val =value_type()) | 构造并初始化n个val |
| vector (const vector& x); (重点) | 拷贝构造 |
| vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构 |
代码演示:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int>v1;//无参构造
vector<int>v2(5, 1);//构造并初始化5个1
for (auto& s : v2)
{
cout << s;
}
cout << endl;
vector<int>v3(v2);//拷贝构造
for (auto& s : v3)
{
cout << s;
}
cout << endl;
vector<int>v4(v2.begin(), v2.end());//使用迭代器
for (auto& s : v4)
{
cout << s;
}
cout << endl;
return 0;
}
vector有关容量的函数
| 容量空间 | 接口说明 |
| size_t size( ) const; | 获取数据个数 |
| size_t capacity() const; | 获取空间容量大小 |
| bool empty() const; | 判空 |
| void resize(size_t n,value_type val=value_type()) | 改变vector的size |
| void reserve(size_t n); | 改变vector的capacity |
代码演示:
#include<iostream>
#include<vector>
using namespace std;
int main1()
{
vector<int>v2(5, 1);//构造并初始化5个1
for (auto& s : v2)
{
cout << s;
}
cout << endl;
cout << v2.size() << endl;
cout << v2.capacity() << endl;
cout << v2.empty() << endl;
v2.resize(16, 2);
cout << v2.capacity() << endl;
for (auto& s : v2)
{
cout << s;
}
cout << endl;
v2.reserve(45);
cout << v2.capacity() << endl;
return 0;
}
注意:以下这段测试vector的默认扩容机制代码在vs在g++下运行结果是不同的,以前我们常说扩容是按两倍扩的,但是在vector中vs下capacity是按1.5倍增长的,g++是按2倍增长的,所以我们不要固定化认为扩容都是两倍增长,具体增长多少也是根据具体需求定义的。
void TestVectorExpand() { size_t sz; vector<int> v; sz = v.capacity(); cout << "making v grow:\n"; for (int i = 0; i < 100; ++i) { v.push_back(i); if (sz != v.capacity()) { sz = v.capacity(); cout << "capacity changed: " << sz << '\n'; } } }reserve只负责开辟空间,如果知道需要多少空间,可以可以提前将空间设置足够, 就可以避免边插入边扩容导致效率低下的问题了。
resize再开空间的同时也会初始化,影响到size。
vector的增删查改
| vector的增删查改 | 接口说明 |
| void push_back(size_t val); | 尾插 |
| void pop_back(); | 尾删 |
| InputIterator find(InputIterator first,InputIterator last,const T&val); | 查找。(不是vector的成员接口,而是通过算法模块实现的) |
| void insert(InputIterator pos,size_t val); | 在pos之前插入 |
| InputIterator erase(InputIterator pos); | 删除pos位置的数据 |
| swap | 交换两个vector数据空间 |
| operator[] | 像数组一样访问 |
代码演示:
int main() { vector<int>v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4);//vector<int> v{1,2,3,4}; for (auto& s : v) { cout << s<<' '; } cout << endl; auto ret = find(v.begin(),v.end(), 3); v.insert(ret, 89); auto it = v.begin(); while (it != v.end()) { cout << *it<<' '; it++; } cout << endl; v.pop_back(); //v.erase(ret); auto pos = find(v.begin(), v.end(), 89); // 删除pos位置的数据 v.erase(pos); for (auto& s : v) { cout << s << ' '; } cout << endl; vector<int>v1; v1.swap(v); for (auto& s : v1) { cout << s << ' '; } cout << endl; for (int i = 0; i < v1.size(); i++) { cout << v1[i] << ' '; } cout << endl; return 0; }
动态二维数组的使用
了解vector类的成员变量:T*_start(指向开头的指针),T*_finish(指向元素结尾的下一个的指针),T*_end_of_storage(指向容量大小的下一个的指针)。

vector<T>实际上是顺序表,顺序表底层是数组。 那么vector<vector<T>>就相当于二维数组。

可以用C++写一下杨辉三角:杨辉三角
二.迭代器失效 (重点)
迭代器底层是一个指针,迭代器失效其实是指迭代器对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,会导致程序崩溃。
对于vector可能导致迭代器失效的操作有:
改变其底层空间的操作都有可能
比如:resize,reserve,insert,assgin、push_back.
v.resize(100, 8):将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容;
v.reserve(100):reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变;
v.insert(v.begin(), 0)/ v.push_back(8):插入元素期间,可能会引起扩容,而导致原空间被释放
v.assgin(100,8): 给vector重新赋值,可能会引起底层容量改变
就比如:
int main()
{
vector<int>v{ 1,2,3,4 };
auto it = v.begin();
v.insert(it, 54);
while (it != v.end())
{
cout << *it << ' ';
it++;
}
cout << endl;
}
运行结果为:

程序崩溃。
insert会导致vector扩容,vector底层原理的旧空间被释放掉了,但是it还指向着那块被释放旧空间,所以在it迭代器操作时,访问了一块被释放的空间,所以程序崩溃。
解决方法为:更新迭代器(重新给it赋值)。

指定位置元素的删除操作--erase
试着判断一下下面这段代码是否能运行成功呢?
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
v.erase(pos);
cout << *pos << endl;
return 0;
}
答案当然是不能了,为什么呢?迭代器pos被删除了,会导致编译器失效。就像如果pos刚好是最后一个元素,删完之后pos刚好是end位置,而end位置没有元素,所以pos迭代器已经被删除,打印*pos就是非法访问。
练习:
删除偶数
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
return 0;
}
判断一下代码是否能运行成功?并思考如果更改才能得到想要的结果。
答案是运行不成功的,因为erase删除了迭代器it,导致迭代器失效,已经失效的迭代器不能进行++,所以会导致程序崩溃。所以我们要怎么做呢?有同学说上面的代码也有问题,如果erase会把后面的数据往前面挪,那么再++会检查不到被删数据后面的一个数据。这是一个问题,但是改正后它还是出错,而且我们还没有更新迭代器。
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it=v.erase(it);//erase的返回值是被删迭代器的下一个迭代器
}
else
++it;
}
return 0;
}
Linux下,g++对迭代器失效检查并不是特别严格。迭代器失效g++不一定会崩溃,只是运行结果不对,但是越界一定会崩溃。
string类也和vector一样,对扩容操作和erase会导致迭代器失效,但都可以通过更新迭代器来解决。
三.vector类的模拟实现

vector是向量类型,它可以容纳许多类型的数据,所以我们需要运用到模板。
vector.h
namespace slm
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
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 _end_of_storage - _start;
}
vector()
{}
~vector()
{
delete[]_start;
_start = _finish = _end_of_storage = nullptr;
}
//v1(v2)
vector(const vector<T>&v)
{
reserve(v.size());
for (auto& e : v)
{
push_back(e);
}
}
void swap(vector<T>& v)
{
std::swap(v._start, _start);
std::swap(v._finish, _finish);
std::swap(v._end_of_storage, _end_of_storage);
}
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
void resize(size_t n, const T& val = T())//T()为匿名对象
{
if (n < size())
{
_finish = _start + n;
}
else//大于或等于
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
T* tmp = new T[n];
//memcpy(tmp,_start,sizeof(T)*oldsize);
//memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另一段内存空间中。
//如果拷贝的是内置类型的元素,memcpy及高效也不会出错,
//但如果是自定义类型元素,并且自定义类型中涉及到资源管理时就会出错,
//因为memcpy的拷贝是浅拷贝。
for (size_t i = 0; i < oldsize; i++)
{
tmp[i] = _start[i];
}
delete[]_start;
_start = tmp;
_finish = _start + oldsize;
_end_of_storage = _start + n;
}
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : 2 * capacity());
}
*_finish = x;
_finish++;
}
void pop_back()
{
assert(_finish > _start);
_finish--;
}
void insert(iterator pos, const T& x)
{
//assert(pos-_start<size())//编译报错:有符号/无符号不匹配
assert(pos<=_finish);
assert(pos >= _start);
//扩容会导致迭代器失效
if (_finish == _end_of_storage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : 2 * capacity());
pos = _start + len;
}
iterator it = _finish - 1;
while (it >= pos)
{
*(it + 1) = *it;
it--;
}
_finish++;
*pos = x;
}
iterator erase(iterator pos)
{
assert(pos <= _finish);
assert(pos >= _start);
iterator it = pos+1;
while (it <= _finish)
{
*(it - 1) = *it;
it++;
}
_finish--;
return pos;
}
private:
iterator _start=nullptr;
iterator _finish=nullptr;
iterator _end_of_storage=nullptr;
};
}
test.cpp
namespace slm
{
void test_vector()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (auto& e : v1)
{
std::cout << e << ' ';
}
std::cout << std::endl;
//测试size
std::cout << v1.size() << std::endl;
//测试capacity
std::cout << v1.capacity() << std::endl;
//测试resize
v1.resize(6, 8);
for (size_t i = 0; i < v1.size(); i++)
{
std::cout << v1[i] << ' ';
}
std::cout << std::endl;
v1.resize(4, 9);
for (size_t i = 0; i < v1.size(); i++)
{
std::cout << v1[i] << ' ';
}
std::cout << std::endl;
//测试拷贝构造函数
vector<int>v2(v1);
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
vector<int> v3;
v3.push_back(5);
v3.push_back(5);
v3.push_back(5);
v3.push_back(5);
v3.push_back(5);
v2 = v3;
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
v2.pop_back();
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
//测试insert
v2.insert(v2.begin() + 2, 9);
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
v2.insert(v2.begin(), 6);
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
v2.insert(v2.end(), 7);
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
//测试erase
v2.erase(v2.end());
v2.erase(v2.begin());
v2.erase(v2.begin() + 2);
for (auto& e : v2)
{
std::cout << e << ' ';
}
std::cout << std::endl;
}
}
int main()
{
slm::test_vector();
return 0;
}
1683






