这里写目录标题
vector简介
vector是表示可变大小数组的序列容器,数组中每个元素必须是同一种类型,并且有一个索引值,通常把vector称为容器。
vector常用接口总览
vector是一个类模板,可保存不同数据类型。
vector
保存string的vector: vector name;
保存int的vector:vector name;
我们在使用vector的时候必须在<>内说明vector内对象的类型
对象定义和初始化
如果只规定了容器元素的个数,没有指定初始化值,标准库会自动提供一个元素初始值。
如果该元素的类型为内置类型,则默认初始化值为0;若为类类型,标准库会用默认构造函数创建初始化。
迭代器相关
容量相关
vector中size和capacity的区别:
size指的是容器内元素的个数,capacity指的是容器内存空间的大小,一般来说capacity要比size大,确保元素个数增加时不需要重新分配内存。
resize 和 reserve
- capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
- reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
- resize在开空间的同时还会进行初始化,影响size。
- 如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够,用reserve,就可以避免边插入边扩容导致效率低下的问题了
增删查改
迭代器相关
vector<int>::iterator iter;
定义了一个迭代器iter,它的数据类型是由vector定义的iterator类型
vector模拟实现过程
各类接口总览
namespace han
{
//模拟实现vector
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//默认成员函数
vector(); //构造函数
vector(size_t n, const T& val); //构造函数
template<class InputIterator>
vector(InputIterator first, InputIterator last); //构造函数
vector(const vector<T>& v); //拷贝构造函数
vector<T>& operator=(const vector<T>& v); //赋值运算符重载
~vector(); //析构函数
//迭代器相关函数
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
//容量和大小相关函数
size_t size()const;
size_t capacity()const;
void reserve(size_t n);
void resize(size_t n, const T& val = T());
bool empty()const;
//修改容器内容相关函数
void push_back(const T& x);
void pop_back();
void insert(iterator pos, const T& x);
iterator erase(iterator pos);
void swap(vector<T>& v);
//访问容器相关函数
T& operator[](size_t i);
const T& operator[](size_t i)const;
private:
iterator _start; //指向容器的头
iterator _finish; //指向有效数据的尾
iterator _endofstorage; //指向容器的尾
};
}
成员变量示意
迭代器相关
//迭代器相关函数
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 _endofstorage - _start;
}
reserve函数
void reserve(size_t n)
{
//reserve只扩容不缩容
if(n>capacity())
{
size_t sz=size(); //记录扩容前容器内元素个数
T* tmp=new T[n];
if(_start) //第一次扩容,_start为空,需判断
{
for(int i=0;i<sz;i++) //不能使用memcpy浅拷贝
{
tmp[i]=_start[i];
}
delete[] _start;
}
_start=tmp;
_finish=_start+sz;
_endofstorage=_start+n;
}
}
resize函数
void resize(size_t n,T val=T())
{
if(n>capacity())
[
reserve(n);
}
//填数据
if(n>size())
{
while(_finish<_start+n)
{
*_finish=val;
_finish++;
}
}
else //删除数据
{
_finish=_start+n;
}
}
内容修改
push_back函数
void push_back(const T& x)
{
if(_finish == _endofstorage) //判断是否需要扩容
{
reserve(capacity()==0?4:2*capacity());
}
*_finish=x;
_finish++;
}
pop_back函数
void pop_back()
{
assert(!empty());
--_finish;
}
insert函数
使用insert函数插入元素时,需要考虑迭代器失效问题,当插入前容器需要扩容时,根据扩容原理,容器的空间地址发生变化,pos的值可能会发生变化,需要根据pos和_start的相对位置更新pos的值,再插入元素。
//pos位置插入val
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos < _finish);
if(_finish==_endofstorage)
{
size_t len=_pos-_start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos=_start+len; //更新pos值
}
iterator end=_finish-1;
while(end>pos)
{
*(end+1)=*(end);
--end;
}
*pos=val;
_finish++;
return pos;
}
erase函数
iterator erase(iterator pos)
{
assert(!empty());
assert(pos >= _start);
assert(pos < _finish);
iterator ch=pos+1;
//pos位置后的数据向前挪动
while(ch<_finish)
{
*(ch-1)=*ch;
ch++;
}
_finish--;
return pos;
}
默认成员
构造函数1
无参构造函数,默认值写在初始化列表中
vector():_start(nullptr),
_finish(nullptr),
_endofstorage(nullptr)
{}
构造函数2
用Input的迭代器 [first,last)初始化,Input可以是任意类型,故使用模板
template <class InputIterator>
vector (InputIterator first, InputIterator last)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
while(first!=last)
{
push_back(*first);
first++;
}
}
构造函数3
构造vector中含有n个值为val的元素,可以先用reserve,再push_back
vector (size_type n, const value_type& val = value_type()):_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; i++) //尾插n个值为val的数据到容器当中
{
push_back(val);
}
}
拷贝构造
vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
_start = new T[v。capacity()];
//将v中的数据一个个拷贝过来
for (size_t i = 0; i < v.size(); i++)
{
//不可以使用memcpy,memcpy只能拷贝内置类型,无法处理自定义类型
_start[i] = v[i]; //如果v中的元素是自定义类型,那么会调用其拷贝构造函数
}
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
拷贝容器内数据时,不能使用memcpy拷贝
当vector中存储的中string的时候,使用memcpy将v4里的每一个string调用拷贝构造,分别拷贝v3里的元素,_str指向同一个string。拷贝好之后释放原容器空间,原容器中存储的每个string在释放的时候会调用string的析构函数,将其指向的字符串也会进行释放,使得vector中每个元素指向的空间就是一块已经被释放的空间,访问该容器的时候也就是对非法空间的访问。
赋值运算符重载
vector<T>& operator=(vector<T> v) //编译器接收右值的时候自动调用其拷贝构造函数
{
swap(v); //交换这两个对象
return *this; //支持连续赋值
}
析构函数
~vector()
{
delete[] _start;
_start=_finish=_endofstorage=nullptr;
}