一,关于vector
vector的数据安排以及操作方式与array数组非常相似,两者的唯一差别在于对空间运用的灵活性。array是静态空间,一旦配置了就不能改变,这也是为什么定义数组时[]里只能给常量不能给变量;vector是动态空间,随着元素的假如,它的内部机制会自行扩充空间以容纳新元素,对于内存的合理利用与运用的灵活性会有很大的帮助。
vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,而且所谓扩充空间,是“配置新空间/数据移动/释放旧空间”的大工程,时间成本很高。
二,vector模拟实现
2.1 基本框架和迭代器实现
#include<iostream>
using namespace std;
template<class T>
class vecctor
{
public:
typedef T* iterator;//定义在这里就可以访问类成员变量了
typedef const T* const_iterator;//const是提供给const对象用的
const_iterator begin()const//end和begin要变成const迭代器,不然就可读可写了
{
return _start;
}
const_iterator end()const
{
return _finish;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
private:
iterator _start; //表示目前使用空间的头
iterator _finish; //表示目前使用空间的尾
iterator _end_of_storage; //表示目前可用空间的尾
};
2.2 构造和析构
vector()
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
vector(int n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)//迭代器区间拷贝构造
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());//复用上面的拷贝构造
swap(tmp);
}
vector<T>& operator= (vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
注意:
vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
如上代码,如果我们把 vector(int n, const T& val = T()) 改为 vector(size_t n, const T& val = T()),会出现构造函数调用不明确的问题,如下
2.3 空间操作
小接口
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
reserve()
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();//提前保存,应对后面的finish更新问题
T* tmp = new T[n];//new会调用构造函数,直接初始化
if (_start)
{
//为了应对vector<vector<int>>的拷贝问题,建议reserve时不用传统的memcpy
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;//释放旧空间
}
_start = tmp;//原来的头指向新空间
//_finish = _start + size();
//finish - start +start=finish = 0,finish就变为nullptr了,所以要在开始就保存size()
_finish = _start + sz;
_end_of_storage = _start + n;
//或者
/*_finish = tmp + size();
_start = tmp;
_end_of_storage = _start + n;*/
}
}
resize()
void resize(size_t n, const T& val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
//初始化填值
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
//+删除数据
_finish = _start + n;
}
}
2.4 []重载
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
2.5 数据操作接口
insert()
insert和erase之后的pos位置,不要直接访问,要更新,不然会出现各种出乎意料的结果 ---- 迭代器失效
iterator insert(iterator pos, const T& x)
{ //这里不能用引用,v.insert(v.begin(),1)时,begin返回_start的临时拷贝,这个拷贝有常性
assert(pos >= _start);
assert(pos <= _finish);
迭代器失效
如果本来有4个数,不扩容,
然后要在pos前面插入一个数,要扩容
然后数据被移到新空间去了,旧数据释放,但是这时候pos仍然指向旧空间的按个位置,变成野指针,发生迭代器失效
// 所以不能简单的用下面的方式扩容,应该用下下面的方式来扩容
//if (_finish == _end_of_storage)
//{
// reserve(capacity() == 0 ? 4 : capacity() * 2);
//}
if (_finish == _end_of_storage)
{
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 = x;
++_finish;
return pos;
}
push_back()
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;//finish位置为最后一个数的下一个位置
++_finish;
//insert(end(), x);
}
pop_back()
void pop_back()
{
assert(_finish > _start);
--_finish;
}
erase()
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
//if (size() < capacity()/2)
//{
// // 缩容 -- 以时间换空间
//}
return pos;
}
三,完整代码
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<malloc.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cbegin()
{
return _start;
}
const_iterator cend() const
{
return _finish;
}
// construct and destroy
vector()
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
vector(int n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());//复用拷贝构造
swap(tmp);
}
vector<T>& operator= (vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
// capacity
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();//提前保存,应对后面的finish更新问题
T* tmp = new T[n];//new会调用构造函数,直接初始化
if (_start)
{
//为了应对vector<vector<int>>的拷贝问题,建议reserve时不用传统的memcpy
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;//释放旧空间
}
_start = tmp;//原来的头指向新空间
//_finish = _start + size();
出bug,finish - start +start=finish = 0,finish就变为nullptr了,所以要在开始就保存size()
_finish = _start + sz;
_end_of_storage = _start + n;
//或者
/*_finish = tmp + size();
_start = tmp;
_end_of_storage = _start + n;*/
}
}
void resize(size_t n, const T& val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
//初始化填值
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
//+删除数据
_finish = _start + n;
}
}
///access///
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
///modify/
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;//finish位置为最后一个数的下一个位置
++_finish;
//insert(end(), x);
}
void pop_back()
{
assert(_finish > _start);
--_finish;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
//insert和erase之后的pos位置,不要直接访问,要更新,不然会出现各种出乎意料的结果 ---- 迭代器失效
iterator insert(iterator pos, const T& x)
{ //这里不能用引用,v.insert(v.begin(),1)时,begin返回_start的临时拷贝,这个拷贝有常性
assert(pos >= _start);
assert(pos <= _finish);
迭代器失效
如果本来有4个数,不扩容,
然后要在pos前面插入一个数,要扩容
然后数据被移到新空间去了,旧数据释放,但是这时候pos仍然指向旧空间的按个位置,变成野指针,发生迭代器失效
// 所以不能简单的用下面的方式扩容,应该用下下面的方式来扩容
//if (_finish == _end_of_storage)
//{
// reserve(capacity() == 0 ? 4 : capacity() * 2);
//}
if (_finish == _end_of_storage)
{
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 = x;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
//if (size() < capacity()/2)
//{
// // 缩容 -- 以时间换空间
//}
return pos;
}
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _end_of_storage; // 指向存储容量的尾
};