实现C++标准库STL小型向量vector

一、 我们知道在C++标准库中有一类很重要的容器:向量vector,其实STL底层实现vector比较复杂,这里我们使用简单的语法实现自己的vector,功能没有标准库的丰富,但包含一些常用的接口,读者也可以自己添加接口和工具函数等。实现过程如下(代码可复现

1)在STL中,所有的容器都是类模板,这里我们也使用类模板,首先看一下定义的外部接口(成员函数):

在这里插入图片描述

2)定义的私有成员:

在这里插入图片描述
在这里插入图片描述

3)此外:我们还定义一些工具函数(被拷贝构造函数、赋值运算符和析构函数使用)

在这里插入图片描述

二、各部分定义:

1、来看一下vector的构造函数:
首先是简单的初始化:将三个指针都初始化为空指针(nullptr),作用相当于合成默认构造器

在这里插入图片描述

第二个是使用一个initializer_list 类型的参数(可变参数),构造MyTinyVec,类似于标准库中的列表初始化(包括直接初始化和拷贝初始化)。

在这里插入图片描述

2、定义拷贝成员(拷贝构造函数、赋值运算符和析构函数)

在这里插入图片描述
在这里插入图片描述

3、其他成员的定义(一些简单的成员即定义为内联函数)

3.1获取元素的数量和容器的容量:
在这里插入图片描述
3.2 获取首元素和尾后元素的指针:
在这里插入图片描述
3.3 重载下标运算符(有const 和非const 的两个版本,重载下标运算符一般都要定义两个):
在这里插入图片描述
3.4 在尾部空的位置插入元素:
在这里插入图片描述
我们可以看到,我们在其中的一些成员函数中使用了一些工具函数,例如free(),check_n_copy()等等,其实我们定义的这些工具才是核心(下附代码

我们在代码中看整个MyTinyVec类的实现(以下为实现部分,代码可复现):

MyTinyVec.hpp  中文件声明和定义
#pragma once
#include <iostream>
#include <memory>
#include <initializer_list>
using namespace std;

template <class T>
class MyTinyVec
{
public:
	MyTinyVec();                           //默认的构造和自定义构造
	MyTinyVec(initializer_list<T> il);
	MyTinyVec(const MyTinyVec &);          //拷贝构造
	MyTinyVec & operator=(MyTinyVec &);    //拷贝赋值
	T& operator[](size_t index);           //重载下标运算符
	const T& operator[](size_t index) const;
	std::size_t size() const;              //获得容器的大小和容量
	std::size_t capacity() const;
	void resreve(unsigned num);            //预留空间
	T * begin();                           //首元素的地址
	T * end();                             //尾元素地址
	void push_back(const T &);
	virtual~MyTinyVec();

private:
	void check_n_alloc();           //检查是否有空间添加新的元素,否则会调用reallocate函数
	std::pair<T *, T *> alloc_n_copy(const T *b,const T *e);    //分配内存,拷贝一个给定范围的元素
	void reallocate();              //重新分配空间
	void free();                    //销毁构造的元素并释放内存
	std::allocator<T> alloc; 
	T *elements;        //首元素指针
	T *first_free;      //最后一个元素的下一位置
	T *cap;             //内存空间的下一位置
};

//构造函数

template <class T>
MyTinyVec<T>::MyTinyVec():elements(nullptr),first_free(nullptr),cap(nullptr) {
}

//自定义构造函数

template<class T>
inline MyTinyVec<T>::MyTinyVec(initializer_list<T> il) {
	pair<T *, T *> newdata = alloc_n_copy(il.begin(), il.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}

//拷贝构造

template<class T>
inline MyTinyVec<T>::MyTinyVec(const MyTinyVec &mv){
	//此时data是一个pair型;first 和 second
	std::pair<T *, T*> data = alloc_n_copy(mv.begin(), mv.end());
	elements = data.first;
	first_free = cap = data.second;
}

//拷贝赋值运算符

template<class T>
inline MyTinyVec<T> & MyTinyVec<T>::operator=(MyTinyVec<T> & mv){
	pair<T *,T *> data = alloc_n_copy(mv.begin(),mv.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}

//重载下标运算符

template<class T>
inline T & MyTinyVec<T>::operator[](size_t index){
	return elements[index];
}

template<class T>
inline const T & MyTinyVec<T>::operator[](size_t index) const
{
	return elements[index];
}

//获取大小

template <class T>
inline std::size_t MyTinyVec<T>::size() const {
	return (first_free - elements);
}

//获取容量

template <class T>
inline std::size_t MyTinyVec<T>::capacity() const {
	return (cap - elements);
}

//预分配空间

template<class T>
inline void MyTinyVec<T>::resreve(unsigned num){
	//auto dataPtr = alloc_n_copy(alloc.begin(),alloc.end());
	//auto dataPtr = alloc.allocate(num);
	cap = elements + num;
}

//获取首元素地址

template<class T>
inline T * MyTinyVec<T>::begin(){
	return elements;
}

//获取尾后元素地址

template<class T>
inline T * MyTinyVec<T>::end(){
	return cap ;
}

//添加元素

template<class T>
void MyTinyVec<T>::push_back(const T &e){
	check_n_alloc();
	alloc.construct(first_free++, e);
}

//析构函数

template <class T>
MyTinyVec<T>::~MyTinyVec() {
	free();
}

//工具函数
//检查是否有空间插入元素

template <class T>
inline void MyTinyVec<T>::check_n_alloc() {
	if (size() == capacity())
		reallocate();
}

//旧空间拷贝至新的内存空间

template<class T>
std::pair<T *, T *> MyTinyVec<T>::alloc_n_copy(const T *b,const T *e){
	T* data = alloc.allocate(e - b);   
	return {data, std::uninitialized_copy(b,e,data)};
}

//重新分配内存空间
//分为三步:1、移动原来的元素进新空间2、释放原空间3、更新指针

template<class T>
inline void MyTinyVec<T>::reallocate(){
	std::size_t newCapacity = this->capacity() * 2;
	auto newData = alloc.allocate(newCapacity);
	auto dest = newData;
	auto elem = elements;
	for (std::size_t i = 0; i < size(); ++i) {
		alloc.construct(dest++,std::move(*elem++));
	}
	free();
	elements = newData;
	first_free = dest;
	cap = elements + newCapacity;
}

//free释放函数

template<class T>
inline void MyTinyVec<T>::free(){
	if (elements) {
		for (T *p = elements; p != first_free;) {
			alloc.destroy(p++);
		}
		alloc.deallocate(elements, cap - elements);
	}
}

最后是测试部分(main函数):
#include "myTinyVec.hpp"

int main() {
	cout << "**************************" << endl;
	cout << "未初始化的string类型的vector,设置预留空间为10:" << endl;
	MyTinyVec<string> svec;
	svec.resreve(10);
	cout << "元素数有:" << svec.size() << endl;
	cout << "容器的容量:" <<svec.capacity() << endl;
	cout << "**************************" << endl;
	cout << "使用列表初始化{1,2,3,4,5}后的int类型vector" << endl;
	MyTinyVec<int> ivec({1,2,3,4,5});
	cout << "元素数有:" << ivec.size() << endl;
	cout << "容器的容量:" << ivec.capacity() << endl;
	for (size_t i = 0; i != ivec.size(); ++i) {
		cout << ivec[i] << " ";
	}
	cout << endl;
	cout << "**************************" << endl;
	cout << "又使用成员push_back插入一个元素后:" << endl;
	ivec.push_back(6);
	cout << "元素数有:" << ivec.size() << endl;
	cout << "容器的容量:" << ivec.capacity() << endl;
	for (size_t i = 0; i != ivec.size(); ++i) {
		cout << ivec[i] << " ";
	}
	cout << endl;
	cout << "**************************" << endl;
	cout << "改变容量为20后:" << endl;
	ivec.resreve(20);
	cout << "元素数有:" << ivec.size() << endl;
	cout << "容器的容量:" << ivec.capacity() << endl;
	for (size_t i = 0; i != ivec.size(); ++i) {
		cout << ivec[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

测试结果如下:
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值