关于vector的介绍:
(1)vector是可变大小的序列容器。
(2)和数组一样支持随机访问
(3)容量的存在为的是留一些额外的空间以适应可能的增长
(4)尾上操作效率相对较高,但是其它位置操作相对较低
构造函数:
(1)vector() 无参构造
(2)vector(size_t n, const T& val=T()) //n个val
(3)vector(const vector& x) //拷贝构造
(4)vector(first, last) //迭代器区间
空间问题:
(1)size() //获取有效数据个数
(2)capacity() //获取容量大小
(3)empty() //判空
(4)resize(size_t n, T val=T()) 改变size
(5)reserve(size_t n) 改变capacity(这两个函数与string的对应规则一样)
需要注意的是:VS(PJ版本)下的capacity是按照1.5倍速度增长,LINUX下(SGI版本)按照2倍速度增长的
增删查改:
(1)push_back(const T& val)//尾插
(2)pop_back()//尾删
(3)insert(iterator pos, const T& val)//在pos位置插入一个数据(因为增容可能会引起迭代器失效)
(4)erase(iterator pos)//删除pos位置数据,返回下一个元素的迭代器(可能会引起迭代器失效)
(5)swap(vector& x)//交换两个vector的数据空间
(6)operator[]//重载[],向数组一样访问
迭代器失效问题:
迭代器指的是迭代器指向的是一块不属于自己的内存,即迭代器对应的指针是“野指针”
vector中迭代器可能失效场景:
(1)所有可能增容的地方(如insert等),因为增容会导致开新空间,释放旧空间,当数据已经被拷贝到新空间后,旧空间已经被释放了,此时迭代器仍然指向的已经被释放的旧空间,这就是所谓的"迭代器失效"。
(2)erase删除一个元素后,导致原来元素的所有迭代器失效。但是erase()返回的是删除后下一个元素的迭代器
解决办法:每次都重新获取迭代器。
vector模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <string.h>
using namespace std;
//模拟实现vector
namespace VECTOR
{
template <class T>//用模板类实现
class Vector
{
public:
//普通迭代器、const迭代器
typedef T* iterator;
typedef const T* const_iterator;
//普通迭代器接口
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
//const迭代器接口
const_iterator begin()const
{
return _start;
}
const_iterator end()const
{
return _finish;
}
Vector()//无参构造函数
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
~Vector()//析构函数
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
//拷贝构造函数
//注意也不能用memcpy,对于内置类型可以,但是vector元素是string的话
//就会出问题
Vector(const Vector<T>& v)
{
_start = new T[v.Capacity()];
for (size_t i = 0; i < v.Size(); i++)
{
_start[i] = v[i];
}
_finish = _start + v.Size();
_endofstorage = _start + v.Capacity();
}
void Swap(Vector<T>& v)
{
swap(_start, v._start);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);
}
//赋值运算符重载(现代写法)
Vector& operator=(Vector<T> v)
{
this->Swap(v);
return *this;
}
//有效元素个数、有效容量
const Size()const
{
return (_finish-_start);
}
const Capacity()const
{
return (_endofstorage-_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];
}
//改变容量
//这种非常容易出错的一个接口,要注意以下几点:
//(1)当需要扩容时,拷数据不能简单的用memcpy,因为memcpy是内存的"浅拷贝",
//如果vector中的元素是string对象时,会使得新空间中的与旧空间对象(_str)
//指向同一块空间,当旧空间释放时,新空间的_str会成为野指针,最后释放
//两次也会导致问题。因此要使用赋值(调用string赋值时的深拷贝)
//(2)扩容前将原来的size值保存下来,因为size的计算依赖于_finish和_start,
//当_start改变后,再利用_finish和_start计算size势必会出现问题
//(3)当原空间元素存在时不为空时才拷数据()否则会相当于对空指针左解引用
void reserve(size_t n)
{
//只有n大于原容量时会去扩容,否则不理睬
if (n > Capacity())
{
size_t size = Size();//获取原来的size
T* tmp = new T[n];
if (size > 0)//有元素才拷
{
for (size_t i = 0; i < size; i++)
{
//如果vector中元素是string对象,这里会
//调用string的赋值运算符重载(进行深拷贝)
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}
//改变有效元素个数(多余的默认用value填充)
void resize(size_t n, const T& value = T())
{
if (n > Capacity())//需要增容
{
reserve(n);
}
if (n < Size())//直接将元素个数减少(该_finish)
{
_finish = _start + n;
}
else//需要填充
{
while (_finish < _start + n)
{
*_finish = value;
++_finish;
}
}
}
//在pos位置前插入一个元素(位置传的是迭代器)
//这个接口也要注意一个问题“迭代器失效”,因此
//将pos位置先得保存一下,然后reserve之后需要重置一下pos
void Insert(iterator pos, const T& x)
{
size_t n = pos - _start;//保存pos到其实距离
if (_finish == _endofstorage)//需要增容
{
size_t newCapacity = (Capacity() == 0 ? 2 : Capacity() * 2);
reserve(newCapacity);
}
pos = _start + n;//将pos重置到新空间的对应位置
//挪数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *(end);
end--;
}
//将这个数据插入到pos位置
*pos = x;
++_finish;
}
//删除pos位置元素
void Erase(iterator pos)
{
assert(pos < _finish && pos >= _start);
iterator cur = pos + 1;//cur指向pos下一个元素
while (cur != _finish)
{
*(cur - 1) = *cur;
++cur;
}
--_finish;
}
private:
T* _start;//指向首元素
T* _finish;//指向尾元素的下一个位置
T* _endofstorage;//指向可用有效空间
};
}