目录
1、在(迭代器)pos位置插入一个元素,并返回指向它的迭代器(insert)
3、删除(迭代器)pos位置的一个元素并返回被删元素的下一个位置的迭代器(erase)
一、vector的核心框架(成员介绍)
在STL之中的vector中,是由三个指针来控制数据的个数以及容量的大小的!
start指针:这个指针指向的是数据的开始位置
finish指针:这个指针指向的是最后一个数据的下一个位置
endOfStorage指针:这个指针指向的是vector所开辟的空间中的最后一个位置
如图所示
而通过这三个指针,我们就能控制数据的个数以及容量的大小
数据的个数:finish指针-start指针就是数据的个数
容量的大小:endOfStorage指针-start指针就是容量的大小
代码如下
size_t capacity()
{
return _endOfStorage - _start;
}
size_t size()
{
return _finish - _start;
}
以上就是vector的核心框架,那么下面就让我们来模拟实现一下
二、迭代器的模拟实现
关于vector的迭代器其实就是一个模板类型指针实现的,因为vector是连续存储数据的,所以不需要对迭代器另外实现,原生指针就可满足需求
而这个迭代器的开始是start指针,结束是finish指针
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
private:
iterator _start;
iterator _finish;
iterator _endOfStorage;
public:
//迭代器
iterator begin()
{
return _start;
}
const_iterator cbegin()const
{
return _start
}
iterator end()
{
return _finish;
}
const_iterator end()const
{
return _finish;
}
};
三、默认成员函数
在写构造函数之前我们先把成员的缺省值设置为空
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
};
1、无参构造
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
vector()
{}
};
无参构造不需要我们显示写,编译器会自动走初始化列表把缺省值赋上
2、由n个值为 x 的元素构造一个vector
我们需要先开辟出一个足够大的空间,再把值赋上即可
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
vector(size_t n , const T& x)
{
_start = new T[n];
for(size_t i = 0 ; i < n ; ++i)
{
*(_start + i) = x;
}
_finish = _endOfStorage = _start + n;
}
};
3、用一段迭代器区间进行构造
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
//此模板类型用于接收迭代器区间
template<class InputIterator>
vector(InputIterator first,InputIterator last)
{
while(first != last)
{
push_back(*first);//此函数是尾插,也就是把数据插入到vector的尾部,后面会实现
++first;
}
}
};
但是此处的迭代器区间构造还有一些问题
思考一下:当我们使用"vector<int> v(8,0)"时会走哪一个构造呢?
'8'和'0'都是int类型的整形,而前面的vector(size_t n,const T& x); n是size_t 类型,编译器不会自动走这个类型,而会走迭代器区间构造这个构造函数
解决办法一、把前面的vector(size_t n , const T& x)改为vector(int n , const T& x);(不推荐)
解决办法二、我们使用函数重载,重新写一个更符合(int,int)类型的函数
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
//此模板类型用于接收迭代器区间
template<class InputIterator>
vector(InputIterator first,InputIterator last)
{
while(first != last)
{
push_back(*first);//此函数是尾插,也就是把数据插入到vector的尾部,后面会实现
++first;
}
}
vector(int n , int x)
{
_start = new T[n];
for(int i = 0 ; i < n ; ++i)
{
*(_start + i) = x;
}
_finish = _endOfStorage = _start + n;
}
};
4、拷贝构造
传统写法:关于拷贝构造我们需要做的动作有两个,第一个是重新开一个一样大的空间(避免浅拷贝),第二是把原数据拷贝给新对象(当然还要考虑自己拷贝自己的情况)
注意:此处拷贝不能用memcpy,因为memcpy是按字节序进行拷贝,如果这里的模板是内置类型,那么memcpy不会出问题,但如果是list,Date,string这类自定义类型,按字节序进行拷贝就会出现浅拷贝问题,浅拷贝问题在string时说过!,所以这里我们需要一个一个进行拷贝!
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
vector(const vector<T>& v)
{
if(v != *this)
{
//1、开空间
_start = new T[v.size()];
//2、赋值
for(size_t i = 0 ; i < v.size() ; ++i)
{
*(_start + i) = *(v._start + i);
}
_finish = _start + v.size();
_endOfStorage = _start + v.capacity();
}
}
};
现代写法:可以直接复用迭代器区间构造,因为传过来的类型也是vector,而既然是vector就有迭代器区间构造,然后我们再写一个交换函数,交换两个vector对象,就实现了深拷贝
代码如下
template<class T>
class vector
{
//...
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
void swap(vector<T>& v)
{
std::swap(_start,v._start);
std::swap(_finish,v._finish);
std::swap(_endOfStorage,v._endOfStorage);
}
vector(const vector<T>& v)
{
if(*this != v)
{
vector<T> tmp(v.begin(),v.end());
swap(tmp);
}
}
};
5、赋值运算符重载
与拷贝构造很类似,这里不再过多说明传统写法,而直接使用现代写法
代码如下:
vector<T>& operator= (vector<T> v)
{
swap(v);
return *this;
}
6、析构函数
析构函数没有什么特别的,直接把创建的数组delete掉即可
~vector()
{
delete[]_start;
_start = _finish = _endOfStorage = nullptr;
}
四、容量相关的接口
1、数据个数(size)
size_t size()
{
return _finish - _start;
}
2、容量大小(capacity)
size_t capacity()
{
return _endOfStorage - _start;
}
3、容量增大至n,不可缩容(reserve)
第一步:新增一个T* 的指针,并开n个空间
第二步:把原数据拷贝至新数组中
第三步:释放原数组
注意:因为我们要释放原数组,而当我们释放完原数组后要把三个成员指针重新赋值,这时我们就需要有size和capacity,由于size和capacity是由三个指针控制的,但此时size和capacity都失效了,因为已知容量是n,那么我们就不需要capacity,我们只需要提前记录一下size即可
void reserve(size_t n)
{
if(n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
for(size_t i = 0 ; i < sz ; ++i)
{
*(tmp + i) = *(_start + i);
}
delete[]_start;
_start = tmp;
_finish = _start + sz;
_endOfstorage = _start + n;
}
}
4、修改vector中的数据数量大小(resize)
在我们模拟实现这个函数之前先了解一下这个接口
1、这个接口的主要作用是用于修改size的大小
2、如果修改的size>capacity,则这个接口也会实现扩容
3、如果修改的size小于当前的size,则会把size减少到修改后的size,并把多出来的元素删除
4、当修改后的size大于当前的size时,这个接口会把size增大为修改后的size,并插入指定的元素值(默认为初始化)
既然我们了解了这些,接下来就让我们来进行模拟实现
void resize(size_t n , const T& x = T())
{
//因为后面要修改finish指针,size是由finish指针计算得来的
//所以我们提前记录下size的大小
size_t sz = size();
if (n > capacity())
{
reserve(n);
}
_finish = _start + n;
if (n > sz)
{
for (size_t i = sz; i < n; ++i)
{
*(_start + i) = x;
}
}
}
五、支持随机访问的接口(运算符[]的重载)
T &operator[](size_t n)
{
return *(_start + n);
}
const T &operator[](size_t n)const
{
return *(_start + n);
}
六、修改元素的接口
1、在(迭代器)pos位置插入一个元素,并返回指向它的迭代器(insert)
注意:因为我们的reserve扩容函数中是把原数组给释放掉了,而pos迭代器指向的还是原数组也就导致pos成为野指针,所以我们需要提前计算好pos的位置,在扩容完成之后把他重新指向新数组的对应位置
iterator insert(iterator pos,const T& x)
{
if(size() == capacity())
{
//计算pos在扩容完后该指向新数组的哪个位置
size_t len = pos - _start;
size_t newcapacity = capacity() == 0 ? 10 : 2*capacity();
reserve(newcapacity);
pos = _start + len;
}
iterator cur = _finish;
while(cur != pos)
{
*cur = *(cur - 1);
--cur;
}
*pos = x;
++_finish;
return pos;
}
2、尾插(push_back)
尾插直接复用insert即可
void push_back(const T& x)
{
insert(_finish,x);
}
3、删除(迭代器)pos位置的一个元素并返回被删元素的下一个位置的迭代器(erase)
iterator erase(iterator pos)
{
iterator cur = pos + 1;
while(cur != _finish)
{
*(cur-1) = *cur;
++cur;
}
--_finish;
return pos;
}
4、尾删
void pop_back()
{
erase(_finish - 1);
}
至此一个支持简单增删查改的vector容器也就实现了!
代码总结
template <class T>
class vector
{
public:
typedef T *iterator;
typedef const T *const_iterator;
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endOfStorage = nullptr;
public:
// 迭代器
iterator begin()
{
return _start;
}
const_iterator cbegin() const
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cend() const
{
return _finish;
}
vector()
{
}
vector(size_t n, const T &x)
{
_start = new T[n];
for (size_t i = 0; i < n; ++i)
{
*(_start + i) = x;
}
_finish = _endOfStorage = _start + n;
}
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first); // 此函数是尾插,也就是把数据插入到vector的尾部,后面会实现
++first;
}
}
vector(int n, int x)
{
_start = new T[n];
for (int i = 0; i < n; ++i)
{
*(_start + i) = x;
}
_finish = _endOfStorage = _start + n;
}
// vector(const vector<T> &v)
// {
// if (v != *this)
// {
// // 1、开空间
// _start = new T[v.size()];
// // 2、赋值
// for (size_t i = 0; i < v.size(); ++i)
// {
// *(_start + i) = *(v._start + i);
// }
// _finish = _start + v.size();
// _endOfStorage = _start + v.capacity();
// }
// }
void swap(vector<T> &v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
}
vector(const vector<T> &v)
{
if (_start != v._start)
{
vector<T> tmp(v.cbegin(), v.cend());
swap(tmp);
}
}
vector<T> &operator=(vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
delete[] _start;
_start = _finish = _endOfStorage = nullptr;
}
size_t size()
{
return _finish - _start;
}
size_t capacity()
{
return _endOfStorage - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T *tmp = new T[n];
for (size_t i = 0; i < sz; ++i)
{
*(tmp + i) = *(_start + i);
}
delete[] _start;
_start = tmp;
_finish = _start + sz;
_endOfStorage = _start + n;
}
}
void resize(size_t n, const T &x = T())
{
size_t sz = size();
if (n > capacity())
{
reserve(n);
}
if (n > sz)
{
for (size_t i = sz; i < n; ++i)
{
*(_start + i) = x;
}
}
}
T &operator[](size_t n)
{
return *(_start + n);
}
const T &operator[](size_t n) const
{
return *(_start + n);
}
iterator insert(iterator pos, const T &x)
{
if (size() == capacity())
{
size_t len = pos - _start;
size_t newcapacity = capacity() == 0 ? 10 : 2 * capacity();
reserve(newcapacity);
pos = _start + len;
}
iterator cur = _finish;
while (cur != pos)
{
*cur = *(cur - 1);
--cur;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
iterator cur = pos + 1;
while (cur != _finish)
{
*(cur - 1) = *cur;
++cur;
}
--_finish;
return pos;
}
void push_back(const T &x)
{
insert(_finish, x);
}
void pop_back()
{
erase(_finish - 1);
}
};
这期vector的模拟实现就到这啦,感谢你的支持,我们下期再见~~