一、vector介绍
vector是表示大小可以更改的数组的序列容器
就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
本质上,vector使用动态分配的数组来存储其元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。如果每次插入新元素都重新开辟一块空间,并且拷贝数据,将会消耗很多的资源。因此每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间更大。
二、vector接口
详情可见vector - C++ Reference (cplusplus.com)https://legacy.cplusplus.com/reference/vector/vector/
1.vector常见构造
函数名称 | 功能说明 |
vector () | 无参构造 |
vector (size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (const vector& x) | 拷贝构造 |
vector (InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
void test_2()
{
vector<int> v1(10, 1);//默认参数构造
vector<int> v2(v1.begin(), v1.end());
for (auto w : v2)
{
cout << w << " ";
}
cout << endl;
string s1("hello world");
vector<char> v3(s1.begin(), s1.end());//迭代器构造
for (auto ch : v3)
{
cout << ch << " ";
}
cout << endl;
}
2.vector容量操作
函数名称 | 功能说明 |
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
reserve | 改变vector的capacity |
resize | 改变vector的size |
void test_4()
{
//如果 n 小于当前容器大小
//则内容将减少到其前 n 个元素,删除超出的元素(并销毁它们)
//如果 n 大于当前容器大小
//则通过在末尾插入所需数量的元素来扩展内容,以达到 n 的大小。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将被值初始化。
//如果 n 也大于当前容器容量
//则会自动重新分配分配的存储空间
vector<int> v1;
cout << v1.size() << endl;
v1.resize(10, 0);
cout << v1.size() << endl;
vector<int> v2(10, 0);
cout << v2.size() << endl;
cout << v2.capacity() << endl;
v2.reserve(15);
cout << v2.capacity() << endl;
}
3.vector访问与遍历
函数名称 | 功能说明 |
operator[] | 像数组一样访问 |
begin+ end | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator |
范围for | C++11支持更简洁的范围for的新遍历方式 |
void test_3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
v1.push_back(9);
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
auto rit = v1.rbegin();
while (rit != v1.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
}
4.vector修改操作
函数名称 | 功能说明 |
push_back | 尾插 |
pop_back | 尾删 |
find | 查找(注意这个是算法模块实现,不是vector的成员接口) |
insert | 在position之前插入val |
erase | 删除position位置的数据 |
swap | 交换两个vector的数据空间 |
void test_5()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
v1.push_back(9);
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);
if (pos != v1.end())
{
v1.insert(pos, 20);
}
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
pos = find(v1.begin(), v1.end(), 3);
if (pos != v1.end())
{
v1.erase(pos);
}
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
v1.erase(v1.begin());
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
}
5.vector迭代器失效问题
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器, 程序可能会崩溃)
vector迭代器失效的操作包括:①引起底层空间改变的操作(resize、reserve、insert、assign、push_back)②指定位置元素的删除操作(erase)
void test_1()
{
vector<int> s1(10, 10);
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
vector<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it<<" ";
it++;
}
s1.push_back(66);
s1.push_back(555);
s1.push_back(4444);
cout << *it << endl;//注意在push_back之后不可以再调用之前的迭代器
//因为迭代器的指向位置改变了
cout << endl;
vector<int>::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *it << " ";//这里会导致迭代器失效
it++;
}
cout << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
}
三、vector模拟实现
#pragma once
#include<iostream>
#include<assert.h>
using std::cout;
using std::endl;
using std::find;
namespace my_vector
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr),_finish(nullptr),_end_of_storage(nullptr)
{
}
~vector()
{
_start = nullptr;
_finish = nullptr;
_end_of_storage = nullptr;
delete[] _start;
delete[] _finish;
delete[] _end_of_storage;
}
//迭代器
iterator begin() { return _start; };
iterator end(){return _finish;}
const_iterator begin()const { return _start; };
const_iterator end()const { return _finish; };
//运算符重构
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(!empty());
--_finish;
}
//size_t find(const T& x)//查找
//{
// iterator head = _start;
// int pos = 0;
// while (head != _finish)
// {
// if (x == *head)
// {
// return pos;
// }
// head++;
// pos++;
// }
// return -1;
//}
iterator erase(iterator pos)//删除pos
{
assert(pos >= _start);
assert(pos <_finish);
iterator start = pos + 1;
while (start != _finish)
{
*(start - 1) = *start;
++start;
}
--_finish;
return pos;
}
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish==_end_of_storage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : 2 * capacity());
pos = _start + len;//扩容后空间为新地址,需要更新pos位置
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
return pos;
}
//容量操作
void reserve(size_t n)//扩容
{
if (n > capacity())
{
T* tmp = new T[n];
size_t sz = size();
if (_start)
{
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
void resize(size_t n,T val=T())
//这里的val为匿名默认构造,从而适应自定义类型
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
size_t capacity()const//容量
{
return _end_of_storage - _start;
}
size_t size()const
{
return _finish - _start;
}
bool empty()
{
return _start == _finish;
}
private:
iterator _start;
iterator _finish;//指向有效元素的下一个位置
iterator _end_of_storage;
};
void test_1()
{
vector<int> v1;
v1.push_back(5);
v1.push_back(4);
v1.push_back(3);
v1.push_back(2);
v1.push_back(1);
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
//cout <<find(7) << endl;//自主实现find测试
}
void test_2()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
v1.pop_back();
v1.pop_back();
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
v1.pop_back();
v1.pop_back();
v1.pop_back();
//v1.pop_back();
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
auto pos = find(v1.begin(), v1.end(), 3);
if (pos != v1.end())
{
v1.insert(pos, 30);
}
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
(*pos)++;
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
}
}
erase操作思路
由于erase是对vector的修改,因此可能会产生迭代器失效问题,解决方法与下面的insert类似。
erase在不同的编译器下报错并不同,但是应当默认eraser之后不要访问pos,pos已经失效
insert操作思路
以下面这段代码为例,find函数返回一个迭代器,如果要对pos位置进行更改可能会出现前面所提及的迭代器指向失效问题
但由于vector中的begin()和end()函数是传值返回,所以在insert的实现中,参数pos不能采用引用传参
因此STL规定通过给insert函数设置返回值的方式来获取pos
void test_3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
auto pos = find(v1.begin(), v1.end(), 3);
if (pos != v1.end())
{
v1.insert(pos, 30);
}
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
(*pos)++;//pos会失效,不能使用
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
}