C++ 模拟实现 STL 中的 vector 与 stack

目录

一,vector

1,vector 的迭代器

2,vector 的数据结构、一些简单功能的实现以及扩容逻辑

3,构造,拷贝与析构

4,插入与删除

5,C++ 11的功能

完整代码:

二,stack


 

一,vector

vector 的数据安排以及操作方式,与 array 非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间,一旦配置了就不能改变,如果想要改变空间,那么得需要用户自己来:首先申请一块新空间,然后将元素从旧空间一一搬往新空间,再把原来的空间释放给系统。

vector 是动态空间,随着新元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此,使用 vector 对于内存的合理利用与运用时的灵活性有很大的帮助,我们再也不必因为害怕空间不足而一开始就申请一个大空间的 array 了。

vector 的实现技术,关键在于其对大小的控制以及重新配置时的数据移动。当 vector 旧的空间已经装满元素时,如果用户每新增一个元素,vector 内部就只是扩充一个元素的空间,这样不是一个明智的做法,因为所谓扩充空间时需要 ①申请新空间 ②移动数据 ③释放旧空间,这是一个大工程,时间成本很高。一会我们就能知道 vector 是怎么在内部进行扩容的了。

关于 vector 的相关操作的使用方法,此文就不一一说明了,此文主要是简单的模拟实现。想要模拟实现一个vector,那就必须得对迭代器有一定的了解,如果不知道迭代器是什么,可以先看看这个:C++ 迭代器与反向迭代器-CSDN博客

 

1,vector 的迭代器

vector 维护的是一个连续线性空间,所以不论其元素类型是什么,普通指针都可以作为 vector 的迭代器而满足所有条件,因为vector迭代器所需要的操作行为,

如:operator*,operator->,operator++,operator--,operator+,operator-operator+=,operator-=,普通指针天生就具备。所以我们可以用原生指针来当 vector 的迭代器。

template<class T>
class vector {
    // ...
public:
    // 使用原生指针作为迭代器
	typedef T* iterator;
	typedef const T* const_iterator;
    // ...
};

2,vector 的数据结构、一些简单功能的实现以及扩容逻辑

vector 所采用的数据结构非常简单:线性连续空间。它用两个迭代器 _start 和 _finish 分别指向申请得来的连续空间中目前已被使用的范围,并以迭代器 _end_of_storage 指向整块连续空间(含备用空间)的尾端:

1645997b422747958dfbbdb635548492.jpeg

template<class T>
class vector {
    // ...
private:
	// 一些成员变量
	iterator _start = nullptr;			// 当前使用的空间的首地址
	iterator _finish = nullptr;			// 当时使用空间的尾地址
	iterator _end_of_storage = nullptr;	// 当前可使用空间的尾地址
    // ...
};

运用 _start、_finish、_end_of_storage 这三个迭代器可以轻松的实现迭代器的 begin() 与 end() 操作以及首尾访问、当前大小、当前容量、判断是否为空、清空、[]重载运算、swap等功能:

template<class T>
class vector {
    // ...
public:
	// 一些迭代器操作
	iterator begin() { return _start; }						/*正向迭代器*/
	iterator end() { return _finish; }
	const_iterator begin()const { return _start; }
	const_iterator end()const { return _finish; }

	typedef Reverse_Iterator<iterator> reverse_iterator;	/*反向迭代器*/
	typedef Reverse_Iterator<const_iterator> const_reverse_iterator;

	reverse_iterator rbegin() { return end(); }		
	reverse_iterator rend() { return begin(); }
	const_reverse_iterator rbegin()const { return end(); }
	const_reverse_iterator rend()const { return begin(); }
    
public:
	size_t size()const { return _finish - _start; }				//有效元素的个数
	size_t capacity()const { return _end_of_storage - _start; }	//容量大小
	bool empty()const { return size() == 0; }					//容器是否为空
	void clear() { _finish = _start; }							//清空有效元素

	//访问元素
	T& back() { return *(end() - 1); }
	const T& back()const { return *(end() - 1); }
	T& front() { return *begin(); }
	const T& front()const { return *begin(); }
	T& operator[](size_t pos) {
		assert(pos < size());
		return *(_start + pos);
	}
	const T& operator[](size_t pos)const {
		assert(pos < size());
		return *(_start + pos);
	}

    // 将当前的 vector 与另一个 vector 交换
    void swap(vector<T>& v) {
	    std::swap(_start, v._start);
	    std::swap(_finish, v._finish);
	    std::swap(_end_of_storage, v._end_of_storage);
    }
    // ...
};

接下来是 vector 的扩容逻辑

为了降低空间配置时的速度成本,vector 实际配置的大小可能比用户的需求更大一些,以备将来可能的扩充。这便是容量(capacity)存在的意义。也就是说,一个 vector 的容量永远大于或等于其元素的个数。一旦元素的个数等于容量,当下次再有新增元素时,整个 vector 就得申请一块更大的新空间。如图:

a28c945ae7a744d6ab423d6e8a9f10de.jpeg

42046feba41d4f81a482c4493789b3ab.jpeg

扩容相关操作的实现:

template<class T>
class vector {
	// ...
public:
	// 扩容的相关操作
    /*预留 n 个元素的空间*/
	void reserve(size_t n) {						
		if (n > capacity()) {
			iterator tmp = new T[n];
			size_t sz = size();
			for (size_t i = 0;i < sz;++i) {
				tmp[i] = _start[i];
			}
			std::swap(tmp, _start);
			_finish = _start + sz;
			_end_of_storage = _start + n;
			delete[] tmp;
		}
	}

    /*将有效元素调节为 n 个*/
	void resize(size_t n, const T& data = T()) {	
		reserve(n);
		if (n > size()) {
			for (size_t i = size();i < n;++i) {
				_start[i] = data;
			}
			_finish = _start + n;
		}
	}
	// ...
};

 

3,构造,拷贝与析构

template<class T>
class vector {
	// ...
public:
	//构造
	vector() {}
	vector(size_t n, const T& data = T()) { resize(n, data); }
	vector(int n, const T& data = T()) { resize(n, data); }

	template<class input_iterator>							/*使用迭代器构造*/
	vector(input_iterator begin, input_iterator end) {
		while (begin != end) {
			push_back(*(begin++));
		}
	}

	//拷贝
	vector(const vector<T>& v) {
		_start = new T[v.capacity()];
		for (size_t i = 0;i < v.size();++i) {
			_start[i] = v[i];
		}
		_finish = _start + v.size();
		_end_of_storage = _start + v.capacity();
	}

	vector<T>& operator=(vector<T> v) {
		swap(v);
		return *this;
	}

	//析构
	~vector() {
		delete[] _start;
		_start = _finish = _end_of_storage = nullptr;
	}

	// ...
};

 

4,插入与删除

STL 中 vector 所提供的相关操作有很多,这里就不一一实现了。

template<class T>
class vector {
	// ...
public:
	//增
    // 在 vector 的尾部新添加一个元素
	void push_back(const T& data) { insert(_finish, data); }

    //在 pos 指向的位置之前插入元素	
	iterator insert(iterator pos, const T& data) {		
		assert(pos >= _start and pos <= _finish);
		if (size() == capacity()) {
			size_t tmp = pos - _start;
			reserve(capacity() == 0 ? 4 : capacity() * 2);
			pos = _start + tmp;
		}
		for (iterator it = _finish;it > pos;--it) {
			*it = *(it - 1);
		}
		++_finish;
		*pos = data;
		return pos;
	}

	//删
    // 删去 vector 头部的一个元素
	void pop_back() { erase(_finish - 1); }

    //删去 pos 指向的元素
	iterator erase(iterator pos) {						
		assert(pos >= _start and pos < _finish);
		for (iterator it = pos + 1;it < _finish;++it) {
			*(it - 1) = *it;
		}
		--_finish;
		return pos;
	}
	// ...
};

 

5,C++ 11的功能

这里主要实现的功能是 initializer_list 与 右值引用,如果对这两个东西不了解的话可以看看这两篇博客:

C++11 一些常用的功能-CSDN博客

C++ 左值引用与右值引用-CSDN博客

① initiallizer_list初始化

vector(const std::initializer_list<T>& list) {
	for (const T& data : list) {
		push_back(data);
	}
}

支持了用initializer_list来构造,我们的vector就可以这样来初始化了:

vector<int> vec = {1,2,3,4,5};

② 右值引用相关

使用右值引用功能可以大大的提高我们代码的运行效率!

template<class T>
class vector {
	// ...
public:
	//移动构造与移动赋值
	vector(vector&& v) :_start(v._start), _finish(v._finish), _end_of_storage(v._end_of_storage) {
		v._start = v._finish = v._end_of_storage = nullptr;
	}

	vector<T>& operator=(vector&& v) {
		delete[] _start;
		_start = _finish = _end_of_storage = nullptr;
		std::swap(_start, v._start);
		std::swap(_finish, v._finish);
		std::swap(_end_of_storage, v._end_of_storage);
		return *this;
	}

	//插入
	void push_back(T&& data) { insert(_finish, std::forward<T>(data)); }

	iterator insert(iterator pos, T&& data) {
		assert(pos >= _start and pos <= _finish);
		if (size() == capacity()) {
			size_t tmp = pos - _start;
			reserve(capacity() == 0 ? 4 : capacity() * 2);
			pos = _start + tmp;
		}
		for (iterator it = _finish;it > pos;--it) {
			*it = *(it - 1);
		}
		++_finish;
		*pos = std::forward<T>(data);
		return pos;
	}
	// ...
};

 

5,完整代码:

namespace mySTL {

	template<class T>
	class vector {

	public:
		
		typedef T* iterator;
		typedef const T* const_iterator;

	private:
		// 一些成员变量
		iterator _start = nullptr;			// 当前使用的空间的首地址
		iterator _finish = nullptr;			// 当时使用空间的尾地址
		iterator _end_of_storage = nullptr;	// 当前可使用空间的尾地址

	public:
		// 一些迭代器操作
		iterator begin() { return _start; }						/*正向迭代器*/
		iterator end() { return _finish; }
		const_iterator begin()const { return _start; }
		const_iterator end()const { return _finish; }

		typedef Reverse_Iterator<iterator> reverse_iterator;	/*反向迭代器*/
		typedef Reverse_Iterator<const_iterator> const_reverse_iterator;

		reverse_iterator rbegin() { return end(); }		
		reverse_iterator rend() { return begin(); }
		const_reverse_iterator rbegin()const { return end(); }
		const_reverse_iterator rend()const { return begin(); }

	public:
		size_t size()const { return _finish - _start; }				//有效元素的个数
		size_t capacity()const { return _end_of_storage - _start; }	//容量大小
		bool empty()const { return size() == 0; }					//容器是否为空
		void clear() { _finish = _start; }							//清空有效元素

		//访问元素
		T& back() { return *(end() - 1); }
		const T& back()const { return *(end() - 1); }
		T& front() { return *begin(); }
		const T& front()const { return *begin(); }
		T& operator[](size_t pos) {
			assert(pos < size());
			return *(_start + pos);
		}
		const T& operator[](size_t pos)const {
			assert(pos < size());
			return *(_start + pos);
		}

		// 将当前的 vector 与另一个 vector 交换
		void swap(vector<T>& v) {
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

	public:
		//构造
		vector(){}
		vector(size_t n, const T& data = T()) { resize(n, data); }
		vector(int n, const T& data = T()) { resize(n, data); }

		template<class input_iterator>							/*使用迭代器构造*/
		vector(input_iterator begin, input_iterator end) {
			while (begin != end) {
				push_back(*(begin++));
			}
		}

		//拷贝
		vector(const vector<T>& v) {
			_start = new T[v.capacity()];
			for (size_t i = 0;i < v.size();++i) {
				_start[i] = v[i];
			}
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}

		vector<T>& operator=(vector<T> v) {
			swap(v);
			return *this;
		}

		//析构
		~vector() {
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}

		
	public:
		//扩容
		void reserve(size_t n) {						/*预留 n 个元素的空间*/
			if (n > capacity()) {
				iterator tmp = new T[n];
				size_t sz = size();
				for (size_t i = 0;i < sz;++i) {
					tmp[i] = _start[i];
				}
				std::swap(tmp, _start);
				_finish = _start + sz;
				_end_of_storage = _start + n;
				delete[] tmp;
			}
		}

		void resize(size_t n, const T& data = T()) {	/*将有效元素调节为 n 个*/
			reserve(n);
			if (n > size()) {
				for (size_t i = size();i < n;++i) {
					_start[i] = data;
				}
				_finish = _start + n;
			}
		}

	public: 
		//增
		// 在 vector 的尾部新添加一个元素
		void push_back(const T& data) { insert(_finish, data); }

		//在 pos 指向的位置之前插入元素	
		iterator insert(iterator pos, const T& data) {
			assert(pos >= _start and pos <= _finish);
			if (size() == capacity()) {
				size_t tmp = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + tmp;
			}
			for (iterator it = _finish;it > pos;--it) {
				*it = *(it - 1);
			}
			++_finish;
			*pos = data;
			return pos;
		}

		//删
		// 删去 vector 头部的一个元素
		void pop_back() { erase(_finish - 1); }

		//删去 pos 指向的元素
		iterator erase(iterator pos) {
			assert(pos >= _start and pos < _finish);
			for (iterator it = pos + 1;it < _finish;++it) {
				*(it - 1) = *it;
			}
			--_finish;
			return pos;
		}


	public:
		// C++11  
		// initializer_list初始化
		vector(const std::initializer_list<T>& list) {
			for (const T& data : list) {
				push_back(data);
			}
		}

		//右值引用相关
		//移动构造与移动赋值
		vector(vector&& v) :_start(v._start), _finish(v._finish), _end_of_storage(v._end_of_storage) {
			v._start = v._finish = v._end_of_storage = nullptr;
		}

		vector<T>& operator=(vector&& v) {
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
			return *this;
		}

		//插入
		void push_back(T&& data) { insert(_finish, std::forward<T>(data)); }

		iterator insert(iterator pos, T&& data) {
			cout << "yes";
			assert(pos >= _start and pos <= _finish);
			if (size() == capacity()) {
				size_t tmp = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + tmp;
			}
			for (iterator it = _finish;it > pos;--it) {
				*it = *(it - 1);
			}
			++_finish;
			*pos = std::forward<T>(data);
			return pos;
		}

	};

}

 

二,stack

stack 是一种先进后出(FirstInLastOut,FILO)的数据结构,它只有一个出口。stack 允许新增元素、移除元素、取得最顶端元素。但除了最顶端外,没有任何其它方法可以存取 stack 的其它元素。也就是说,stack 不能遍历。将元素推入 stack 的操作称为 push,将元素推出 stack 的操作称为 pop。

e2239d802750415c8fad6d4802ea0871.jpeg

std::stack 是 STL(标准模板库)提供的容器适配器之一。容器适配器(Container Adapter)是 C++ STL(标准模板库)中的概念,它们是基于现有的容器提供的接口进行封装和扩展的类模板。容器适配器允许以不同的方式访问现有容器的元素,并提供了特定的操作和功能。简单来说,stack 就是靠封装其他容器来实现的。

stack 的底层实现会根据用户的选择来选择底层容器,我们可以选择 vector,也可以选择 deque 或者是 list。

namespace mySTL {

	template<class T, class Container = vector<T>>
	class stack {
	private:
		Container _cont;	// 底层使用的容器
	public:

		void push(const T& data) { _cont.push_back(data); }
		void push(T&& data) { _cont.push_back(std::forward<T>(data)); }
		void pop() { _cont.pop_back(); }

		T& top() { return _cont.back(); }
		const T& top()const { return _cont.back(); }

		size_t size() { return _cont.size(); }
		bool empty() { return _cont.empty(); }

	};

}

 

总之,vector 是一种动态数组,适用于需要高效的随机访问和在末尾插入/删除元素的场景。
stack 是一种后进先出的容器,适用于需要保持元素按照特定顺序访问的场景。
无论是 vector 还是 stack,它们都是 STL 提供的方便且易于使用的数据结构,能够简化和加速开发过程。

 

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值