实现前需要思考的一个问题
为什么需要将空间的申请与对象的构建分开
查看vector的模板参数时可以看到其有第三个参数是空间适配器allocator,查找其对外提供的成员函数不难发现它的实现逻辑是将空间的申请与对象的构建分开的,为什么呢?不弄清楚这个问题显然我们是无法自定义实现一个vector的。
我们都知道,对于vector而言,其有一个预留空间,函数是reserve。
这是因为STL中存放的是大量元素,如果每次创建一个对象就申请一次空间,这样的话效率非常低下。所以可以直接一次性申请大片空间,然后在申请的空间上进行对象的构建,这样效率更快。
代码实现
#include <iostream>
#include <memory>
#include <algorithm>
using namespace std;
template<typename T>
class Vector{
public:
typedef T* iterator; //让我们写的这个类型也能通过迭代器进行访问
Vector()
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
}
~Vector();
void push_back(const T& value);
void pop_back();
int size() const;
int capacity() const;
//迭代器指针
iterator begin(){
return _start;
}
iterator end(){
return _finish;
}
private:
void reallocate();//重新分配内存,动态扩容要用的
private:
//进行空间申请与释放,以及对象构建与销毁的类
static std::allocator<T> _alloc;
T* _start; //指向数组中的第一个元素
T* _finish; //指向最后一个实际元素之后的那个元素
T* _end_of_storage; //指向数组本身之后的位置
};
//静态成员必须在类外进行初始化
template <typename T>
std::allocator<T> Vector<T>::_alloc;
//重新分配内存,动态扩容要用的
template <typename T>
void Vector<T>::reallocate(){
//1、获取新空间大小
//先存下来原来的容量
int oldCapacity = capacity();
//然后再将新的空间按两倍的标准进行扩容
//如果oldCapacity为0,那么新空间大小为1,否则就按两倍扩容
int newCapacity = 2 * oldCapacity > 0 ? 2*oldCapacity : 1;
//2、申请新的对应大小的空间
T* _ptmp = _alloc.allocate(newCapacity);
//将老空间里面的元素拷贝到新的空间里面来
if(_start){//判断老空间里面是否存在元素
//若有数据,那么执行拷贝
//copy(_start,_finish,_ptmp); 这个函数有缺陷
//我们让其在未初始化的空间上拷贝对象
uninitialized_copy(_start, _finish, _ptmp);
//拷贝完先执行销毁对象的操作
while(_finish != _start){
//老的空间上的对象一个个进行销毁
_alloc.destroy(--_finish);
}
//然后销毁老的空间,也就是回收老的空间
_alloc.deallocate(_start,oldCapacity);
}
//三个指针之前是指向老的空间,然后扩容之后需要将三个指针与新的空间产生联系
_start = _ptmp;
_finish = _start + oldCapacity;
_end_of_storage = _start + newCapacity;
}
template <typename T>
Vector<T>::~Vector(){
if(_start){
while(_finish!=_start){
_alloc.destroy(--_finish);
}
_alloc.deallocate(_start,capacity());
}
}
//插入元素
template <typename T>
void Vector<T>::push_back(const T& value){
//首先判断vector是不是满的
if(size() == capacity()){
//如果满了,那么扩容
reallocate();
}
//否则没满
//那么当小于容量大小的时候,往vector里面送值
if(size() < capacity()){
//把新的value对象送到vector的末尾
//然后_finish++继续指到最后一个实际元素的下一个位置
_alloc.construct(_finish++,value);
}
}
template <typename T>
void Vector<T>::pop_back(){
//判断是否为空,不为空才删除一个末尾元素
if(size() > 0){
//_finish先减减来到实际元素的位置然后再执行删除
_alloc.destroy(--_finish);
}
}
//记录元素个数
template <typename T>
int Vector<T>::size() const{
//因为指针是被STL的迭代器封装过的,
//所以直接进行四则运算就可以得到正常的数值
return _finish - _start;
}
//记录容量
template <typename T>
int Vector<T>::capacity() const{
return _end_of_storage - _start;
}
template <typename Container>
void printCapacity(const Container& con){
cout << "con.size()" << con.size() << endl;
cout << "con.capacity()" << con.capacity() << endl;
}
void test(){
Vector<int> number;
printCapacity(number);
cout << endl;
number.push_back(1);
printCapacity(number);
cout << endl;
number.push_back(2);
printCapacity(number);
cout << endl;
number.push_back(3);
printCapacity(number);
cout << endl;
number.push_back(4);
printCapacity(number);
cout << endl;
number.push_back(5);
printCapacity(number);
for(auto& elem : number){
cout << elem << " ";
}
cout << endl;
}
int main(){
test();
return 0;
}
总结
其实根据vector的源码可以大概实现上面的效果,真正复杂的位置是空间配置器类alloc的实现,上述代码中我们是直接调用的该类的各种API。
如果有时间的话其实可以好好研读一下alloc的源码,看它是如何为我们的容器分配空间和回收空间的,最好是读一读《STL源码剖析》这本书,会对STL有更深刻的体会。