自定义vector的实现

本文解析了STL中vector类的设计,重点在于其如何通过将空间申请与对象构建分开,以提高性能。通过reserve函数预分配空间并使用内存适配器allocator实现动态扩容,这有助于减少频繁的内存分配操作。作者还提供了vector的代码实现和测试示例,鼓励读者深入理解空间配置器类alloc的工作原理。
摘要由CSDN通过智能技术生成

实现前需要思考的一个问题

为什么需要将空间的申请与对象的构建分开

查看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有更深刻的体会。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在地球迷路的怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值