vector的简单实现及常用接口
目录
1.vector类的介绍
2.vector类常用的接口
2.1 vector类常用构造函数
2.2 vector类常用迭代器
2.3 vector类空间接口
2.4 vector的增删查改
3.vector类的简单实现
3.1 vector类结构以及构造及析构实现
3.2 string类常用接口实现
4.迭代器失效
5.insert的修改
1.vector类的介绍
vector类代表的是大小可以变化的数组,常见的有,增,删,查,改几个接口(功能),以及vector迭代器的使用。
2.vector类常用的接口
2.1 vector类常用构造函数
构造函数 | 接口说明 |
vector() | 无参构造 |
vector(size_t n,const T& val=T())(注:T代表模版) | 构造并初始化n个val |
vector(const vector<T>& v) | 拷贝构造 |
vector (InputIterator first, InputIterator last) | 使用迭代器进行初始化构 造 |
#include<vector>
#include<iostream>
#include<string>
using namespace std;
int main()
{vector<int> v;//无参构造vector<int> v1(10, 1);//初始化10个1vector<int> v2(v1);//拷贝构造string s;vector<int> v3(s.begin(), s.end());//利用迭代器初始化return 0;
}
vector类使用时一定要实例化
2.2 vector类常用迭代器
iterator的使用 | 接口说明 |
begin+end | begin获取第一个数据位置的iterator/const_iterator end获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin+rend | begin获取最后一个数据位置的iterator/const_iterator end获取第一个数据的前一个位置的iterator/const_iterator |
#include<vector>
#include<iostream>
using namespace std;
int main()
{vector<int> myvector;//插入数据for (int i = 1; i <= 5; i++) myvector.push_back(i);//正向迭代器for (vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)cout << ' ' << *it;cout << '\n';//反向迭代器for (vector<int>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); ++it)cout << ' ' << *it;cout << '\n';return 0;
}
2.3 vector类空间接口
容量空间 | 接口说明 |
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize | 改变vector的size |
reserve | 改变vector的capacity |
注:vs下capacity是按1.5倍增长的,g++是按2倍增长的。
reserve(size_t res_arg=0) 预留空间,不改变有效元素个数,当 reserve 的参数小于 string 的底层空间总大小时, reserver 不会改变容量大小。
resize(size_t n) 与 resize(size_t n,T val=T()) 都是将vector有效数据改变到 n 个,不同的是当n>capacity时 resize(n) 用 val来填充多出的元素空间,若没有写val的参数,使用val类型的默认构造的缺省值
注意: resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
#include<vector>
#include<iostream>
#include<string>
using namespace std;
template<class T>
void Print(const vector<T>& x)
{for (auto e : x){cout << e << " ";}cout << endl;
}
int main()
{vector<int> v(10, 1);cout << v.size() << " " << v.capacity() << " ";Print(v);//打印v.resize(5);//当n小于capacitycout << v.size() << " " << v.capacity() << " ";Print(v);v.resize(15, 2);//当n大于capacity,多的空间用val填入cout << v.size() << " " << v.capacity() << " ";Print(v);v.resize(25);//使用val类型的默认构造的缺省值cout << v.size() << " " << v.capacity() << " ";Print(v);v.reserve(20);//当n小于capacity时,不变cout << v.size() << " " << v.capacity() << " ";Print(v);v.reserve(30);cout << v.size() << " " << v.capacity() << " ";Print(v);return 0;
}
当确定vector存储元素的个数时,可以利用reserve提前开好一定的空间,防止多次扩容
2.4 vector的增删查改
函数名称 | 接口说明 |
push_back | 尾插 |
pop_back() | 尾删 |
insert | 在pos位置前插入数据 |
erase | 删除pos位置的数据 |
swap | 交换两个vector的存储空间 |
operator[ ] | 和数组一样访问 |
#include<vector>
#include<iostream>
#include<string>
using namespace std;
template<class T>
void Print(const vector<T>& x)
{for (auto e : x){cout << e << " ";}cout << endl;
}
int main()
{vector<int> v;//尾插v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);//尾删v.pop_back();v.pop_back();Print(v);//插入v.insert(v.begin()+2, 20);//第一个参数是迭代器类型Print(v);//删除v.erase(v.end()-1);//参数是迭代器类型Print(v);//数组访问v[1] = 10;Print(v);//交换数据vector<int> v1(10, 1);Print(v);Print(v1);v.swap(v1);Print(v);Print(v1);return 0;
}
3.vector类的简单实现
3.1 vector类结构以及构造及析构实现
vector内的数据类型可能是int,double,对于这种不确定是那种类型时,可以利用模版来解决,而vector的结构由三个迭代器组成,而迭代器的底层就是指针
namespace chuxin
{template<class T>class vector{public:typedef const T* const_iterator;typedef T* iterator;private:iterator _start=nullptr;iterator _finish=nullptr;iterator _end_of_storage=nullptr;};
}
对于默认构造,由于此时给了成员变量的缺省值,而且默认构造要走初始化列表,默认构造就不需要写任何内容
对于用迭代器来构造vector,可以利用模版,类模板的成员函数,还可以继续是函数模版
vector()//默认构造
{}
vector(const vector<T>& v)//拷贝构造函数
{reserve(v.size());//开辟空间for (auto e : v){push_back(e);//一个一个数据插入}
}
// 类模板的成员函数,还可以继续是函数模版
//利用迭代器来构造
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
//构造并初始化
vector(size_t n, const T& val = T())//T()代表用T类型的默认构造来做缺省值
{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}
~vector()//析构
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}
3.2 string类常用接口实现
4.2.1 size,capacityt ,operator[ ],clear,begin,end,赋值=
size,capacityt ,operator[ ],clear,begin,end这几个比较简单,不做叙述
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;}T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}void clear()//清理数据{_finish = _start;}
对于赋值=,可以利用swap交换来使用
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
vector<T>& operator=(vector<T>& v)//赋值
{swap(v);return *this;
}
3.2.2 reserse(扩容)
对于扩容,这里有一些关于深浅拷贝的问题,来看一下代码
void reserve(size_t n)//扩容
{if (n > capacity()){size_t old_size = _finish - _start;//记录数据iterator tmp = new T[n];memcpy(tmp, _start, old_size * sizeof(T));_start = tmp;_finish = tmp + old_size;_end_of_storage=tmp + n;}
}
void reserve(size_t n)//扩容
{if (n > capacity()){size_t old_size = _finish - _start;//记录数据iterator tmp = new T[n];for (size_t i = 0; i < old_size; i++)//深拷贝{tmp[i] = _start[i];}_start = tmp;_finish = tmp + old_size;_end_of_storage=tmp + n;}
}
3.2.3 empty,pop_back,push_back
bool empty()const
{return _finish == _start;
}
void pop_back()//尾删
{assert(_finish != _start);--_finish;
}
void push_back(const T& x)//尾插
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}
3.2.4 insert
iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}iterator end = _finish - 1;while (pos <= end){*(end + 1) = *end;//移动数据--end;}*pos = x;++_finish;return pos;
}
这里的insert有些问题,下文解释
3.2.5 erase
void erase(iterator pos)
{assert(pos < _finish);assert(pos >= _start);iterator it = pos + 1;while (it != end()){*(it-1) = *it;++it;}--_finish;
}
4.迭代器失效
来看下列代码
void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);//打印int x;cin >> x;auto p = find(v.begin(), v.end(), x);if (p != v.end()){v.insert(p, 20);(*p) *= 10;}Print(v);
}
p原本指向的是2,当插入20时,p的指向确变成了插入的数据,此时p的位置没有改变,这就是一个迭代器失效,因此insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值
void test_vector1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);Print(v);int x;cin >> x;auto p = find(v.begin(), v.end(), x);if (p != v.end()){// insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值/*v.insert(p, 20);(*p) *= 10;*/p = v.insert(p, 40);(*(p + 1)) *= 10;}Print(v);}
}
同时当插入一个数时,进行了扩容,由于旧空间会被释放,此时的迭代器由于没有更新,变成了野指针,不能直接使用
void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)//删除偶数v.erase(it);++it;}prinCon(v);//打印
}
对于这份代码,发现连续的偶数无法删除,当遇到偶数时,删除,此时迭代器失效,没有改变位置,对迭代器++,就会跳过连续的偶数,如果pos刚好是最后一个元素,删完之后pos刚好是end 的位置,而end位置是没有元素的,那么pos同样也失效了,因此可以对迭代器进行更新
void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)it = v.erase(it);else++it;}prinCon(v);
}
5.insert的修改
由于知道扩容会对迭代器失效,在某一位置插入数据时,可以记录一下迭代器,避免迭代器失效
iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;//更新迭代器}iterator end = _finish - 1;while (pos <= end){*(end + 1) = *end;//移动数据--end;}*pos = x;++_finish;return pos;
}