C++之vector
1.vector介绍
- vector表示可变大小数组的序列容器
- 采用连续的空间存储元素,并且大小是动态可以改变的。
- vector使用动态分配数组来存储元素,当插入新元素时,这个数组要分配一个新的数组增大空间,再将所有元素移至新的数组中。
- 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
2.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) | 使用迭代器进行初始化构造 |
vector()
:start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{}
vector(size_t n,const T& value= T)
:start(new T[n])
, finish(start)
, end_of_storagr(start+n)
{
for (size_t i = 0; i < n;i++){
*finish++ = value;
}
}
vector(const vector<T>& v))
:start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{
vector<T> temp(v.cbegin(),v.cend());
this->swap(temp);
}
template<class Iterator>
vector(Iterator first,Iterator last){
size_t n = bite::disstance(first,last);
start = new T[n];
finish = start;
end_of_storage = start + n;
while (first != last)
{
*finish = *first;
finish++;
first++;
}
vector<T>& operator=(vector<T> v)
{
this->swap(v);
return *this;
}
~vector()
{
if (start)
{
delete[] start;
start = nullptr;
finish = nullptr;
end_of_storage = nullptr;
}
}
}
2.vector iterator 的使用
iterator使用 | 接口说明 |
---|
begin + end | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator | rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |
如下图;
iterator begin(){
retirn start;
}
iterator end(){
return finish;
}
reverse_iterator rbegin()
{
return end();
}
reverse_iterator rend()
{
return begin();
}
3.vector 空间增长问题
容量空间 | 接口说明 |
---|
size | 获取数据个数 | capacity | 获取容量大小 | empty | 判断是否为空 | resize | 改变vector的size | reserve | 将vector放入capacity |
size_t size()const{
return finish - start;
}
size_t capacity()const{
return end_of_storage - start;
}
bool empty()const{
return finish == start;
}
void resize(size_t newsize,const T& value=T()){
size_t oldsize = size();
if (newsize>oldsize){
size_t oldcapacity = capacity();
if (newsize>oldcapacity){
reserve(newsize);
}
for (size_t i = oldsize; i < newsize;++i){
start[i] = value;
}
}
finish = start + newsize;
}
void reserve(size_t newcapacity){
size_t oldcapacity = capacity();
size_t n = size();
if (newcapacity>oldcapacity){
T* temp = new T[newcapacity];
if (start){
for (size)t i = 0; i < n;i++){
temp[i] = start[i];
}
}
start = temp;
finish = start + n;
end_of_storage = start + newcapacity;
}
}
4.vector增删查改
vector增删查改 | 接口说明 |
---|
push_back | 尾插 | pop_back | 尾删 | find | 查找(自己实现,不是vector成员接口) | insert | 在position之前插入val | erase | 删除position未知的数据 | swap | 交换两个vector的数据空间 | operator[] | 重载使像数组一样访问 |
T& operator[](size_t index){
assert(index<size());
return start[index];
}
const T& operator[](size_t index)const
{
assert(index<size());
return start[index];
}
T& front(){
return *begin();
}
const T& front()const
{
return *begin();
}
T& back(){
return *(finish-1);
}
const T& back()const
{
return *(finish - 1);
}
void push_back(const T& value){
if (finish==end_of_storage){
reserve(capacity()*2+3);
}
*finish = value;
++finish;
}
void pop_back(){
if (empty()){
return;
}
--finish;
}
iterator insert(interator pos,const T& value){
if (pos<start||pos>finish){
return pos;
}
if (finish==end_of_storage){
reserve(capacity()*2);
}
auto it = finish - 1;
while (it>=pos){
*(it + 1) = *it;
it--;
}
*ppos = value;
++finish;
return pos;
}
iterator erase(iterator pos){
if (pos<start||pos>=finish){
return end();
}
auto it = pos + 1;
while (it<finish){
*(it - 1) = *it;
it++;
}
finish--;
return pos;
}
void clear(){
finish = start;
}
5.vector的扩容机制
看以下代码:
void TestPushBack(){
vector<int> v;
size_t sz = v.capacity();
for (size_t i = 0; i < 100;i++){
v.push_back(i);
if (sz!=v.capacity()){
sz = v.capacity();
cout << sz << endl;
}
}
}
结论:
- vs中vector是按照1.5倍的方式扩容,Linux下vector是按照2倍的方式扩容
- 如果在使用vector之前,可以预估vector中大概可以放多少个元素,最好提前将空间开辟好,否则vector是边插入边扩容,成本很大。
3.迭代器失效
1.什么是迭代器
- 迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装
2.为什么迭代器会失效
- 比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间(迭代器对应的指针指向了非法空间),造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。
3.哪些操作会导致迭代器失效
- 所有可能会导致扩容操作的方法都可能会导致迭代器失效,如:push_back、insert、assign、reserve、resize、operator=
void TestIterator(){
vector<int> v{1,2,3,4,5,6};
vector<int>::iterator it = v.begin();
v.push_back(7);
v.push_back(8);
v.push_back(9);
while (it != v.end()){
cout << *it << " ";
++it;
}
cout << endl;
}
原因:上面说过,vector增加元素需要扩容,而扩容会重新分配一个新的空间,而不是原来的指向,扩容成功后原来的空间就失效了
解决方法:
void TestIterator(){
vector<int> v{1,2,3,4,5,6};
vector<int>::iterator it = v.begin();
v.push_back(7);
v.push_back(8);
v.push_back(9);
it = v.begin();
while (it != v.end()){
cout << *it << " ";
++it;
}
cout << endl;
}
- iterator erase(iterator position)、iterator erase(iterator first,iterator last),这些进行删除操作的时候
void TestErase(){
vector<int> v{1,2,3,4,5,6,7};
auto it = v.begin();
while (it!=v.end()){
v.erase(it);
++it;
}
}
原因:直接删除it会导致指向消失,再++的时候就无法找到这块空间
解决方案
oid TestErase(){
vector<int> v{1,2,3,4,5,6,7};
auto it = v.begin();
while (it!=v.end()){
it = v.erase(it);
}
}
即vs中,erase将position迭代器位置的元素删除后,该position迭代器就i失效了,erase返回下一个元素的位置
- swap,在交换的时候不注意迭代器的指向也容易出错
void TestSwap()
{
vector<int> v1{1,2,3,4,5};
vector<int> v2{6,7,8,9,0};
auto it1 = v1.begin();
auto it2 = v2.begin();
v1.swap(v2);
while (it1!=v1.end()){
cout << *it1 << " ";
++it1;
}
cout << endl;
while (it2 != v2.end()){
cout << *it2 << " ";
++it2;
}
cout << endl;
}
原因:swap之后,v1与v2的迭代器指针的指向也已经交换了,如果还指向原来的迭代器,指向的就是非法空间
解决方案:
void TestSwap()
{
vector<int> v1{1,2,3,4,5};
vector<int> v2{6,7,8,9,0};
auto it1 = v1.begin();
auto it2 = v2.begin();
v1.swap(v2);
it1 = v1.begin();
it2 = v2.begin();
while (it1!=v1.end()){
cout << *it1 << " ";
++it1;
}
cout << endl;
while (it2 != v2.end()){
cout << *it2 << " ";
++it2;
}
cout << endl;
}
|