zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

向量类模板的声明和实现---扩充版本

2023-03-14 22:51:05 时间

Vector.hpp

#pragma once
#include<cstdlib>
#define SPACE_MAX 16  //表示数组的最小长度
template<class T>
class Vector
{
private:
	T* data;//维护动态数组的指针
	int size;//数组的数据元素的个数
	int max;//当前数组最大能容纳的元素个数
	void Error(const char* cs)const { cout << cs << endl; exit(1); }//错误信息报告
public:
	//这里的构造函数,里面的形参n,决定了当前数组的长度,但为了防止长度不够用,减少扩容次数,这里会给n+ SPACE_MAX作为数组最大容纳元素个数
	explicit Vector(int n = 0) :size(0), max(n + SPACE_MAX),data(NULL)
	{
		if (max > 0)
			data = new T[max];
	};
	//复制构造函数
	Vector(const Vector& v):data(NULL),max(0)
	{
		//这里用深拷贝---防止堆区内存重复释放
		data = new T[v.max];
		size = v.size;
		max = v.max;
		for (int i = 0; i < size; i++)
		{
			data[i] = v.data[i];
		}
	}
	//析构函数
	~Vector()
	{
		delete[] data;
	}
	//赋值运算符重载
	Vector& operator=(const Vector<T>& v);
	//下标运算符重载
	T& operator[](int id)
	{
		return data[id];
	}
	//常量型下标运算符重载---只有后置const可以作为重载条件,前置不行
	const T& operator[](int id)const
	{   
		return data[id];
	}
	//判断是否为空
	bool Empty()const
	{
		return size == 0;
	}
	//求当前容器中元素个数
	int Size()const
	{
		return size;
	}
	//求数组最大的容量
	int Max()const
	{
		return max;
	}
	//清空数组
	void Clear()const
	{
		size = 0;
	}
	//迭代器
	typedef T* iterator;
	//const型迭代器
	typedef const T* const_iterator;
	//返回开始迭代器
	iterator Begin()
	{
		return data;
	}
	const_iterator Begin()const
	{
		return data;
	}
	//返回结束迭代器
	iterator End()
	{
		return data+size;
	}
	const_iterator End()const
	{
		return data + size;
	}
	//返回首元素的引用
	const T& Front()const
	{
		return data[0];
	}
	T& Front()
	{
		return data[0];
	}
	//返回尾元素的引用
	const T& Back()const
	{
		return data[size - 1];
	}
     T& Back()
	{
		return data[size - 1];
	}
	 //尾插
	 void Push_back(const T& item)
	 {
		 data[size++] = item;
	 }
	 //尾删
	 void Pop_back()
	 {
		 size--;
	 }
	 //扩容函数
	 void Reserve(int newMax);
	 //增值函数
	 void resize(int newSize, const T& item);
	 //插入函数--插入到指定迭代器之前,返回的迭代器指向新元素所处位置
	 iterator Insert(iterator itr, const T& item);
	 //删除函数----删除迭代器指向位置的数据,返回迭代器,但此时迭代器指向的值应该是未删除前位置的后一个位置元素
	 iterator Erase(iterator itr);
};
//赋值运算符重载
template<class T>
Vector<T>& Vector<T>::operator=(const Vector<T>& v)
{
	//先判断两个容器最大值是否相等
	if (max != v.max)
	{
		//深拷贝防止堆区内存重复释放
		delete[] data;
		max = v.max;
		data = new T[max];
	}
	size = v.size;
	for (int i = 0; i < size; i++)
		data[i] = v[i];
	return *this;
}
//扩容函数
template<class T>
void Vector<T>::Reserve(int newMax)
{
	//如果当前newMax比max还小,拿扩了个寂寞,当然直接返回喽!
	if (newMax < max) return;
	//进行扩容操作--开辟新空间存放原来的数据
	//我们要先保留原数组
	T* old = data;
	data = new T[newMax];
	for (int i = 0; i < size; i++)
		data[i] = old[i];
	//除了新的数组最大容量需要更新,别的不需要
	max = newMax;
	delete[] old;
}
//增值函数
template<class T>
void Vector<T>::resize(int newSize, const T& item)
{
	//如果新增元素个数比最大容量都多,要干嘛?----扩容啊,愣着干嘛呢!
	if (newSize > max)
		Reserve(newSize * 2 + 1);
	//把新增加的元素初始化为item
	for (int i = size; i < newSize; i++)
		data[i] = item;
	size = newSize;
}
//插入函数--插入到指定迭代器之前,返回的迭代器指向新元素所处位置
//这里注意typename---因为
template<class T>
typename Vector<T>::iterator Vector<T>::Insert(iterator itr, const T& item)
{
	//容量已满,要先扩容
	if (size == max)
	{
		Reserve(size * 2 + 1);
	}
	//把要插入位置之后的元素统统后移一位,从数组最后一个元素开始一个个往后移
	for (iterator p = data + size, q = data + size - 1; p != itr; --p, --q)
	{
		//p迭代器在q迭代器之后
		//注意原itr位置的元素也要往后移,这就是为什么结束条件是p!=itr
		*p = *q;
	}
	*itr = *item;
	size++;
	return itr;
}
//删除函数----删除迭代器指向位置的数据,返回迭代器,但此时迭代器指向的值应该是未删除前位置的后一个位置元素
template<class T>
typename Vector<T>::iterator Vector<T>::Erase(iterator itr)
{
	//从删除位置开始的后一个元素依次前移一位,直到数组最后一个元素移动后,结束
	for (iterator p = itr, q = itr + 1; q != data + size; ++p, ++q)
	{
		*p = *q;
	}
	size--;
	return itr;
}

main.cpp

#include<iostream>
using namespace std;
#include"Vector.hpp"
template<class iterator>
void display(iterator first,iterator last)
{
	for (; first != last; ++first)
	{
		cout << *first << " ";
	}
	cout << endl;
}
void test()
{
	Vector<int> v;
	for (int i = 0; i < 10; i++)
		v.Push_back(i);
	//这里类型已经确定了,就不用在通过typename来声明类型
	Vector<int>::iterator itr = v.Begin();
	cout << "after operator++: " << endl;
	cout << *(++itr) << endl;
	cout << "after operator--" << endl;
	cout << *(--itr) << endl;
	v.Erase(itr);
	cout << "after eraseing the first: " << endl;
	display(v.Begin(), v.End());
	v.Pop_back();
	cout << "after erasing the last: " << endl;
	cout << v.Front() << endl;
	cout << v.Back() << endl;

	Vector<int> v1 = v;
	v1.Reserve(20);
	v1.resize(20, 5);
	cout << "v1.size= " << v1.Size() << endl;
	for (int i = 0; i < v1.Size(); ++i)
		v1[i]++;
	cout << "after reserve(20),resizing(20,5) and adding(1): " << endl;
	display(v1.Begin(), v1.End());
	cout << "v1.size= " << v1.Size() << endl;
}
int main()
{
	test();
	return 0;
}

补充:删除[first,last)区间的数据,返回当前数据的位置的erase重载函数。

代码:

template<class T > typename Vector<T>::iterator Vector<T>::Erase(iterator first, iterator last)
{
	//计算删除的元素个数
	int num = last - first;
     //删除指定区间里面的元素
	if (last != End())
	{
		//计算剩余需要移动的元素个数
		int leftNum = End() - last;
		iterator p = first, q = last;
		for (int i = 0; i < leftNum; i++,++p,++q)
		{
			*p = *q;
		}
	}
	size -= num;
	//返回first迭代器之前的一个元素
	return first;
}

推荐下面一种写法:

template<class T > typename Vector<T>::iterator Vector<T>::Erase(iterator first, iterator last)
{
	iterator p = first;
	iterator q = last;
	for (; q != data + size; ++p, ++q)
		*p = *q;
	size -= last - first;
	return first;
}

补充:在pos处插入另一个Vector容器指定区间[first, last)的数据的函数

代码:

//在pos处插入另一个Vector容器指定区间[first, last)的数据
template<class T> void Vector<T>::Insert(iterator pos, iterator first, iterator last)
{
	//计算需要插入的元素个数
	int num = last - first;
	if (num + size > max)
		Reserve((num + size) * 2);
	//先把从pos位置开始的元素后移num个位置
	for (iterator p = End(), q = End() + num; p != pos - 1; --p, --q)
		*q = *p;
	//从pos位置开始,依次插入num个新元素
	for (int i = 0; i < num; i++)
		*pos++ = *first++;
	size += num;
}

补充: 交换两个Vector中的数据—swap函数

代码:

//交换两个Vector中的数据
template<class T>
void Vector<T>::Swap(Vector<T>& v)
{
	//交换指针的指向
	T* temp = data;
	data = v.data;
	v.data = temp;
	//交换元素个数
	int t = size;
	size = v.size;
	v.size = t;
	//交换数组容量
	int m = max;
	max = v.max;
	v.max = m;
}

推荐写法—该写法需注意深拷贝问题:

//交换两个Vector中的数据
template<class T>
void Vector<T>::Swap(Vector<T>& v)
{
	Vector<T> temp = *this;
	*this = v;
	v=temp;
}

注意:

  • 其实在C++Primer书上的P593页下半部分,有解释的,C++语言默认情况下,假定通过作用域运算符访问的名字不是类型,所以当我们要访问的是类型时候,必须显示的告诉编译器这是一个类型,通过关键字typename来实现这一点
  • 类模板继承时,如果无法直接使用父类函数和变量,需要加作用域

typename用法大佬的文章详细讲解