C++模拟实现vector容器【万字模拟✨】

更多精彩内容.....

🎉❤️播主の主页✨😘

Stark、-CSDN博客

本文所在专栏:

学习专栏C语言_Stark、的博客-CSDN博客

项目实战C系列_Stark、的博客-CSDN博客

数据结构与算法_Stark、的博客-CSDN博客

座右铭:梦想是一盏明灯,照亮我们前行的路,无论风雨多大,我们都要坚持不懈。


根据文档,我们先看一下vector有哪些成员,需要我们完成什么功能。如果你对这些功能有过初步的了解请跳过。【vector - C++ Reference (cplusplus.com)】。


模拟过程

1、新建模拟实现vector类工程项目:

创建新项目->选择C++空项目模板->选择存储路径并修改项目名称为”模拟实现vector类”

2、在“模拟实现vector类”工程项目下新建源程序文件(vector.cpp)和头文件(vector.h)

头文件(.h)

编写vector.h头文件代码

#pragma once

#include <iostream>

#include <cstdlib>

#include <cstring>

#include <initializer_list>

using namespace std;

PS:首先使用pragma预处理指令声明头文件只被包含一次,避免重复包含带来的问题;然后使用include预处理指令包含本程序需要使用的头文件。最后避免每次都使用命名空间的域访问问题,我们使用using将std命名空间进行展开。

namespace str {//自己定义一个命名空间,避免vector与std::vector的二义性
    template<class T>//使用模板进行泛型编程,增强代码复用性。
    class vector {//定义vector类
    public:
        typedef T* iterator;//将T*类型的指针重命名为iterator(迭代器)。//此处为模拟实现,实际的迭代器为一个特殊的类
        typedef const T* const_iterator;//将const T*类型的常指针重命名为const_iterator
    protected:
        iterator _start = nullptr;//线性表的首地址
        iterator _finish = nullptr;//线性表的尾地址的下一个地址
        iterator _end_of_storage = nullptr;//线性表可访问的最大有效地址
    public:
        //成员函数
    }
}
第一部分:成员函数

本部分声明的函数欲实现功能:初始化顺序表,销毁顺序表。包括无参构造函数,拷贝构造函数,其它构造函数,析构函数,以及赋值运算符重载

#pragma region Member functions

vector() :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}//1.无参构造,使用初始化参数列表对成员变量进行赋初值:nullptr

~vector();//2.析构函数

vector(int num, const T& _Val = T());//3.线性表开辟num个空间,并全部赋值为_Val

vector(const vector<T>& _Right);//4.拷贝构造函数,直接使用同类型的其它变量引用进行赋值

vector(std::initializer_list<T> _Ilist);//5.列表初始化构造,即花括号构造

template<class InputIterator> vector(InputIterator _First, InputIterator _Last);//6.使用其它类型构造vector,需要传入其他类型的首尾指针

vector<T>& operator=(const vector<T>& _Right);//7.赋值运算符重载使用const&类型可以接收左值和右值

vector<T>& operator=(std::initializer_list<T> _Ilist);//8.赋值运算符重载,传入列表进行赋值

#pragma endregion

PS:本部分是进行对象构造(construct)与销毁(destruct)的成员函数声明。第一个函数使用到了初始化参数列表初始化成员变量的知识。第三个函数_Val使用默认参数。第六个函数使用到了模板类内定义模板函数的知识点。

第二部分:元素访问

本部分函数声明欲实现功能:获取第i个位置的值,修改第i个值,扩展性补充访问首位元素。包括下标访问运算符重载,索引访问函数at(),首元素访问front(),尾元素访问back()。

#pragma region Elements access

const T& operator[](size_t _Pos)const;//1.重载[]下标访问运算符,

const T& at(size_t _Pos)const;//2.索引访问函数

const T& front()const;//3.访问首元素

const T& back()const;//4.访问尾元素

//const_iterator data()const;

#pragma endregion

PS:四个函数均使用&类型,做到了访问的同时可以修改。加上const可以接收左值或右值。

第三部分:迭代器

本部分声明的函数欲实现包括获取首元素地址,尾元素地址等。

#pragma region Iterators

iterator begin();//获取首元素地址

iterator end();//获取尾元素地址

//reverse_iterator rbegin();

//reverse_iterator rend();

const_iterator cbegin();//获取首元素地址常量

const_iterator cend();//获取尾元素地址常量

//const_reverse_iterator crbegin();

//const_reverse_iterator crend();

#pragma endregion

第四部分:容量

本部分声明的函数欲实现的功能有:判断顺序表是否为空,获取顺序表的长度。包括:判空函数empty(),大小函数size(),空间函数capacity(),扩容函数resize()和reserve()。

#pragma region Capacity

bool empty()const;

size_t size()const;

size_t capacity()const;

void resize(size_t _Newsize);//扩size

void reserve(size_t _Newcapacity);//扩capacity

//void shrink_to_fit();

#pragma endregion

PS:三、四部分的实现为遍历顺序表提供基础。同时扩容函数reserve函数为增加元素(插入)提供了很大的帮助。

第五部分:修改

本部分声明的函数欲实现的功能有:插入元素(包括指定位置插入insert()和尾插push_back()),删除函数(包括指定位置删除erase()和尾删pop_back()),清空顺序表(将顺序表置空)clear(),扩展性实现有交换两个顺序表swap()。

#pragma region Midifier

void assign(const_iterator _First,const_iterator _Last);

void push_back(const T& _Val);

void pop_back();

iterator insert(const_iterator _Where,const T& _Val);

iterator erase(const_iterator _Where);

void swap(vector<T>& _Right);

void clear();

//emplace();

//emplace_back();

#pragma endregion

//最后写一个测试使用的打印函数

void ShowInfo() {
	iterator it = _start;
	while (it < _finish) {
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

源程序文件(.cpp)

编写vector.cpp源程序文件代码

函数1:empty()
	template<class T>
	bool vector<T>::empty()const {
		return _start == nullptr;
	}

PS:直接将顺序表的首地址与空指针进行等于条件判断,如果是空返回True反之False。不涉及成员变量的修改,所以将函数使用const修饰,后续不再对const修饰成员函数进行说明。

函数2:size()
	template<class T>
	size_t vector<T>::size() const {
		return(_finish - _start);
	}

PS:使用顺序表尾地址减去顺序表首地址,得到的就是元素个数,直接返回。

函数3:capacity()
	template<class T>
	size_t vector<T>::capacity()const {
		return (_end_of_storage - _start);
	}

PS:使用顺序表可访问最大地址减去顺序表首地址,得到的就是空间容量,直接返回。

函数4:reserve()
	template<class T>
	void vector<T>::reserve(size_t _Newcapacity) {
		vector<T> old = *this;
		_start = new T[_Newcapacity];
		memcpy(_start, old._start, sizeof(T) * old.size());
		_finish = _start + old.size();
		_end_of_storage = _start + _Newcapacity;
	}	

PS:创建一个临时vector容器old存储本身数据在内的所有信息。使用new在堆区开辟出新容量大小的空间,存储到_start中。使用memcpy库函数将数据拷贝过来。将_finish设为_start+old.size();_end_of_storage设为_start+_Newcapacity。这样的话就解决了容器空间不够的问题,但此处存在一个隐患,就是迭代器失效的问题。会在很多地方考虑到这种情况,遇到时再作说明。

函数5:begin()
	template<class T>
	typename vector<T>::iterator vector<T>::begin() {
		return _start;
	}

PS:返回元素的迭代器首位置,直接将顺序表的首地址_start进行返回,无需判断顺序表是否为空,因为如果顺序表为空表,那么_start存储的也是空指针,自行返回nullptr。

函数6:end()
	template<class T>
	typename vector<T>::iterator vector<T>::end() {
		return _finish;
	}

PS:返回元素的迭代器的末尾的后一个位置,也就是_finish。无需判空,原因如上。

函数7:cbegin()
	template<class T>
	typename vector<T>::const_iterator vector<T>::cbegin() {
		const_iterator _cbegin = _start;
		return _cbegin;
	}

PS:返回元素的迭代器的的首地址的常迭代器,也就是const _start。提前将const _start存储到常迭代器类型中的一个变量_cbegin中,然后返回。也可以选择使用C++提供的强制类型转换增加_start的const属性。

函数8:cend()
	template<class T>
	typename vector<T>::const_iterator vector<T>::cend() {
		const_iterator _cend = _finish;
		return _cend;
	}

PS:返回元素的迭代器的末尾的后一个地址的常迭代器,也就是const _finish。处理方法同上。

函数9:operator[]()重载
	template<class T>
	const T& vector<T>::operator[](size_t _Pos)const {
		if (_Pos < 0 || _Pos >= size())return -1;
		return _start[_Pos];
	}

PS:访问第_Pos个元素,并返回该元素的引用。先进行位置有效性判断,看其是否在0和size()之间。如果不在,规定返回-1。否则访问*(_start+_Pos)即_start[_Pos]。

函数10:at()
	template<class T>
	const T& vector<T>::at(size_t _Pos) const {
		if (_Pos < 0 || _Pos >= size())return -1;
		return _start[_Pos];
	}

PS:访问第_Pos个元素,并返回该元素的引用。先进行位置有效性判断,看其是否在0和size()之间。如果不在,规定返回-1。否则访问*(_start+_Pos)即_start[_Pos]。

函数11:front()
	template<class T>
	const T& vector<T>::front()const {
		if (empty())return -1;
		return *_start;
	}

PS:访问第1个元素,也就是顺序表首地址_start地址存放的元素。如果不为空就通过解引用符*返回。否则返回-1。如果不进行判断将会导致访问nullptr的非法操作。

函数12:back()
	template<class T>
	const T& vector<T>::back()const {
		if (empty())return -1;
		return *(_finish - 1);
	}

PS:访问最后一个元素,也就是顺序表尾地址_finish-1地址存放的元素。如果不为空就通过解引用符*返回。否则返回-1。如果不进行判断将会导致访问nullptr的非法操作。

函数13:assign()
	template<class T>
	void vector<T>::assign(const_iterator _First, const_iterator _Last) {
		size_t _size = _Last - _First;
		_start = new T[_size];
		for (int i = 0; i < _size; i++) {
			_start[i] = _First[i];
		}
		_finish = _start + _size;
		_end_of_storage = _finish;
	}

PS:记录传入的两个指针的间隔,即_size。使用new在堆区开辟_size块空间(总字节为_size*sizeof(T)),将开辟的空间的首地址记录在_start中。利用循环,将传入的指针区域内的所有值依次赋给*(_start+i),即_start[i]。为顺序表尾指针_finish赋值_start+_size,顺序表最大可访问地址_end_of_storage赋值_finish。

函数14:erase()
	template<class T>
	typename vector<T>::iterator vector<T>::erase(const_iterator _Where) {
		if (_Where < _start || _Where >= _finish)return nullptr;
		for (iterator it = const_cast<iterator>(_Where); it < _finish - 1; ++it) {
			*it = *(it + 1);
		}
		_finish--;
		return const_cast<iterator>(_Where);
	}

PS:对传入的地址进行判断,如果不在_start和_finish之间(左闭右开),那么返回空指针。否则进行删除操作:利用for循环,从_Where处开始到_finish-2为止。将下一个地址指向的元素覆盖到该位置,该位置的元素即被删除。循环的目的是将后续元素整体向左移动一位。最后将_finish--,尾指针向前移动,原末未被覆盖的尾元素不可访问。利用const_cast<iterator>将_Where强制取消const属性后的指针返回。

函数15:pop_back()
	template<class T>
	void vector<T>::pop_back() {
		erase(_finish - 1);
		//if(empty())return;
		//_finish--;
	}

PS:复用erase()函数,传入_finishi-1尾元素地址。另一种思路是,判断_finish是不是空的,如果不是_finish--,直接让有效访问区间在末尾减小一位,反之直接return;返回。

函数16:clear()
	template<class T>
	void vector<T>::clear() {
		_end_of_storage = _finish = _start = nullptr;
	}

PS:将顺序表的三个指针置空就达到了目的。此时会造成内存泄露的问题,在此之前我们可以进行delete[] _start;当然为了保险起见,我们需要在进行delete[] 释放之前加上判断_start是否为nullptr的操作。这里有个注意点,就是delete后面的[]不可省略,因为在开辟堆区空间时开辟的是一段连续空间。(new value_type()与delete value_name匹配,new value_type[]与delete[] value_name匹配,在C++中也可以使用C语言的库函数malloc与free,但不能与C++的new与delete混搭,因为C++的new是malloc+抛异常,delete是free+抛异常。二者作用类似,但机制不同)

函数17:swap()
	template<class T>
	void vector<T>::swap(vector<T>& _Right) {
		vector<T> tmp(*this);
		*this = _Right;
		_Right = tmp;
	}

PS:此处我们采取中间变量进行交换两个顺序表。过程为顺序表1->temp,顺序表2->顺序表1,temp->顺序表2。

函数18:push_back()
	template<class T>
	void vector<T>::push_back(const T& _Val) {
		insert(_finish, _Val);
	}

PS:我们复用下面的insert()函数直接在尾指针处插入元素。

函数19:insert()
	template<class T>
	typename vector<T>::iterator vector<T>::insert(vector<T>::const_iterator _Where, const T& _Val) {
		if (_Where< _start || _Where>_finish)return nullptr;//无效范围,插入失败
		iterator _pos = const_cast<iterator>(_Where);
		int gap = _pos - _start;
		while (_finish >= _end_of_storage) {
			reserve(capacity() == 0 ? 4 : 2 * capacity());
			_pos = _start + gap;
		}
		for (iterator it = _finish; it > _pos; --it) {
			*it = *(it - 1);
		}
		*_pos = _Val;
		_finish++;
		return _pos;//返回随机迭代器
	}

PS:在指定位置插入元素的整体步骤为:定义一个T类型指针it从顺序表尾指针_finish开始,将前一个指针it-1指向的元素移动到当前位置,循环直到it到达_Where结束。然后就可以修改_Where处的元素值,将欲插入的元素直接覆盖未移动走的遗留元素,达到插入的目的。到目前位置,整体过程就叙述完毕。但是,由于要考虑空间大小是否足够的问题,我们就需要进行扩容,while(_finish>=_end_of_storage)reserve(capacity()==0? 4 : 2*capacit())。复用reserve函数进行二倍扩容,考虑到初始可能为0,我们默认赋予4个字节。该扩容过程一直循环_end_of_storage > _finish。此时会出现迭代器失效的问题,所以我们利用记录当前位置指针到表头指针的距离gap,每次扩容都将_pos(=_Where)更新为_start+gap。这样迭代器就随着失效而不断更新有效了。

函数20:~vector();
	template<class T>
	vector<T>::~vector() {
		if (_start)delete[] _start;
		_start = nullptr;
		_finish = nullptr;
		_end_of_storage = nullptr;
	}

PS:该函数为类的析构(_destruct)函数。具体实现代码类似于clear()函数。

函数21:vector(const vector<T>& _Right);与vector<T>& operator=(const vector<T>& _Right);
	template<class T>
	vector<T>& vector <T>::operator=(const vector<T>& _Right) {
		size_t s = _Right._finish - _Right._start;
		_start = new T[s];
		for (int i = 0; i < s; i++) {
			*(_start + i) = *(_Right._start + i);
		}
		_finish = _start + _Right.size();
		_end_of_storage = _start + _Right.capacity();
		return *this;
	}

	template<class T>
	vector<T>::vector(const vector<T>& _Right) {
		size_t s = _Right._finish - _Right._start;
		_start = new T[s];
		for (int i = 0; i < s; i++) {
			*(_start + i) = *(_Right._start + i);
		}
		_finish =_start + _Right.size();
		_end_of_storage =_start + _Right.capacity();
	}

PS:二者实现的功能一样,类似于assign()函数,只是传参不同,一个是部分区间,这两个是整段区间。对于构造函数无返回值,赋值运算符重载时需要返回*this。

函数22:vector(std::initializer_list<T> _Ilist);vector<T>& operator=(std::initializer_list<T> _Ilist);
	template<class T>
	vector<T>::vector(std::initializer_list<T> _Ilist) {
		size_t _size = _Ilist.size();
		_start = new T[_size];
		_finish = _start;
		for (T e : _Ilist) {
			*_finish = e;
			_finish++;
		}
		_end_of_storage = _start + _size;
	}

	template<class T>
	vector<T>& vector<T>::operator=(std::initializer_list<T> _Ilist) {
		size_t _size = _Ilist.size();
		_start = new T[_size];
		_finish = _start;
		for (T e : _Ilist) {
			*_finish = e;
			_finish++;
		}
		_end_of_storage = _start + _size;
		return *this;
	}

PS:二者实现的功能一样,类似于assign()函数,只是传参不同,一个是部分区间,这两个是元素列表,例如{1,2,5,6,8}就是一个简单的_Ilist。对于构造函数无返回值,赋值运算符重载时需要返回*this。

函数23: template<class T> vector<T>::vector(int num, const T& _Val)
	template<class T>
	vector<T>::vector(int num, const T& _Val) {
		_start = new T[num];
		for (int i = 0; i < num; i++) {
			_start[i] = _Val;
		}
		_finish = _start + num;
		_end_of_storage=_start + num;
	}

PS:使用new在堆区开辟num块空间,每块空间由new自动推算(为sizeof(T)),首地址存储在_start中,然后依次访问这num个空间,赋予同样的初值 _Val,如果该值不传,自动调用T类型的构造函数使用类型默认值进行初始化。最后将_finish和_end_of_storage设置为_start+num即可。

函数24:
template<class T>
template<class InputIterator>
vector<T>::vector(InputIterator _First, InputIterator _Last) {
    size_t _size = _Last - _First;
    _start = new T[_size];
    _finish = _start;
    for (int i = 0; i < _size; i++) {
        _start[i] = _First[i];
        _finish++;
    }
    _end_of_storage = _finish;
}

PS:该构造函数是为了利用其他容器的指针(模拟迭代器)将其它容器的一段区间复制到该类型的顺序表中。赋值方法不再赘述。


最终代码文件(.hpp)

#pragma once
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
using namespace std;

namespace str {

	template<class T>
	class vector {
	public:				
		typedef T* iterator;
		typedef const T* const_iterator;
#pragma region Member functions
		vector() :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
		~vector();
		vector(int num, const T& _Val = T());
		vector(const vector<T>& _Right);
		vector(std::initializer_list<T> _Ilist);
		template<class InputIterator> vector(InputIterator _First, InputIterator _Last);
		vector<T>& operator=(const vector<T>& _Right);
		vector<T>& operator=(std::initializer_list<T> _Ilist);
#pragma endregion
#pragma region Elements access 
		const T& operator[](size_t _Pos)const;
		const T& at(size_t _Pos)const;
		const T& front()const;
		const T& back()const;
		//const_iterator data()const;
#pragma endregion
#pragma region Iterators
		iterator begin();
		iterator end();
		//reverse_iterator rbegin();
		//reverse_iterator rend();
		const_iterator cbegin();
		const_iterator cend();
		//const_reverse_iterator crbegin();
		//const_reverse_iterator crend();
#pragma endregion
#pragma region Capacity
		bool empty()const;
		size_t size()const;
		size_t capacity()const;
		void resize(size_t _Newsize);//扩size
		void reserve(size_t _Newcapacity);//扩capacity
		//void shrink_to_fit();
#pragma endregion
#pragma region Midifier
		void assign(const_iterator _First,const_iterator _Last);
		void push_back(const T& _Val);
		void pop_back();
		iterator insert(const_iterator _Where,const T& _Val);
		iterator erase(const_iterator _Where);
		void swap(vector<T>& _Right);
		void clear();
		//emplace();
		//emplace_back();
#pragma endregion
		void ShowInfo() {
			iterator it = _start;
			while (it < _finish) {
				cout << *it << " ";
				it++;
			}
			cout << endl;
		}
	protected:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
	/*----------------------------------------------------------------------------*/
#pragma region FunctionCodes

	//Capccity
	template<class T>
	bool vector<T>::empty()const {
		return _start == nullptr;
	}
	template<class T>
	size_t vector<T>::size() const {
		return(_finish - _start);
	}
	template<class T>
	size_t vector<T>::capacity()const {
		return (_end_of_storage - _start);
	}

	template<class T>
	void vector<T>::reserve(size_t _Newcapacity) {
		vector<T> old = *this;
		_start = new T[_Newcapacity];
		memcpy(_start, old._start, sizeof(T) * old.size());
		_finish = _start + old.size();
		_end_of_storage = _start + _Newcapacity;
	}		
	template<class T>
	void vector<T>::resize(size_t _Newsize) {

	}
	/*----------------------------------------------------------------------------*/

	//Elements access
	template<class T>
	const T& vector<T>::operator[](size_t _Pos)const {
		if (_Pos < 0 || _Pos >= size())return -1;
		return _start[_Pos];
	}
	template<class T>
	const T& vector<T>::at(size_t _Pos) const {
		if (_Pos < 0 || _Pos >= size())return -1;
		return _start[_Pos];
	}
	template<class T>
	const T& vector<T>::front()const {
		if (empty())return -1;
		return *_start;
	}
	template<class T>
	const T& vector<T>::back()const {
		if (empty())return -1;
		return *(_finish - 1);
	}
	/*----------------------------------------------------------------------------*/

	//Iterators
	template<class T>
	typename vector<T>::iterator vector<T>::begin() {
		return _start;
	}
	template<class T>
	typename vector<T>::iterator vector<T>::end() {
		return _finish;
	}
	template<class T>
	typename vector<T>::const_iterator vector<T>::cbegin() {
		const_iterator _cbegin = _start;
		return _cbegin;
	}
	template<class T>
	typename vector<T>::const_iterator vector<T>::cend() {
		const_iterator _cend = _finish;
		return _cend;
	}
	/*----------------------------------------------------------------------------*/

		//Modifier
	template<class T>
	void vector<T>::assign(const_iterator _First, const_iterator _Last) {
		size_t _size = _Last - _First;
		_start = new T[_size];
		for (int i = 0; i < _size; i++) {
			_start[i] = _First[i];
		}
		_finish = _start + _size;
		_end_of_storage = _finish;
	}

	template<class T>
	typename vector<T>::iterator vector<T>::insert(vector<T>::const_iterator _Where, const T& _Val) {
		if (_Where< _start || _Where>_finish)return nullptr;//无效范围,插入失败
		iterator _pos = const_cast<iterator>(_Where);
		int gap = _pos - _start;
		while (_finish >= _end_of_storage) {
			reserve(capacity() == 0 ? 4 : 2 * capacity());
			_pos = _start + gap;
		}
		for (iterator it = _finish; it > _pos; --it) {
			*it = *(it - 1);
		}
		*_pos = _Val;
		_finish++;
		return _pos;//返回随机迭代器
	}

	template<class T>
	void vector<T>::push_back(const T& _Val) {
		insert(_finish, _Val);
	}

	template<class T>
	typename vector<T>::iterator vector<T>::erase(const_iterator _Where) {
		if (_Where < _start || _Where >= _finish)return nullptr;
		for (iterator it = const_cast<iterator>(_Where); it < _finish - 1; ++it) {
			*it = *(it + 1);
		}
		_finish--;
		return const_cast<iterator>(_Where);
	}
	template<class T>
	void vector<T>::pop_back() {
		erase(_finish - 1);
		//if(empty())return;
		//_finish--;
	}

	template<class T>
	void vector<T>::clear() {
		_end_of_storage = _finish = _start = nullptr;
	}

	template<class T>
	void vector<T>::swap(vector<T>& _Right) {
		vector<T> tmp(*this);
		*this = _Right;
		_Right = tmp;
	}
	/*----------------------------------------------------------------------------*/

	//Member functions
	template<class T>
	vector<T>& vector <T>::operator=(const vector<T>& _Right) {
		size_t s = _Right._finish - _Right._start;
		_start = new T[s];
		for (int i = 0; i < s; i++) {
			*(_start + i) = *(_Right._start + i);
		}
		_finish = _start + _Right.size();
		_end_of_storage = _start + _Right.capacity();
		return *this;
	}
	template<class T>
	vector<T>& vector<T>::operator=(std::initializer_list<T> _Ilist) {
		size_t _size = _Ilist.size();
		_start = new T[_size];
		_finish = _start;
		for (T e : _Ilist) {
			*_finish = e;
			_finish++;
		}
		_end_of_storage = _start + _size;
		return *this;
	}


	template<class T>
	vector<T>::vector(int num, const T& _Val) {
		_start = new T[num];
		for (int i = 0; i < num; i++) {
			_start[i] = _Val;
		}
		_finish = _start + num;
		_end_of_storage=_start + num;
	}
	template<class T>
	vector<T>::vector(const vector<T>& _Right) {
		size_t s = _Right._finish - _Right._start;
		_start = new T[s];
		for (int i = 0; i < s; i++) {
			*(_start + i) = *(_Right._start + i);
		}
		_finish =_start + _Right.size();
		_end_of_storage =_start + _Right.capacity();
	}
	template<class T>
	vector<T>::vector(std::initializer_list<T> _Ilist) {
		size_t _size = _Ilist.size();
		_start = new T[_size];
		_finish = _start;
		for (T e : _Ilist) {
			*_finish = e;
			_finish++;
		}
		_end_of_storage = _start + _size;
	}


	template<class T> 
	template<class InputIterator> 
	vector<T>::vector(InputIterator _First, InputIterator _Last) {
		size_t _size = _Last - _First;
		_start = new T[_size];
		_finish = _start;
		for (int i = 0; i < _size; i++) {
			_start[i] = _First[i];
			_finish++;
		}
		_end_of_storage = _finish;
	}
	
	template<class T>
	vector<T>::~vector() {
		if (_start)delete[] _start;
		_start = nullptr;
		_finish = nullptr;
		_end_of_storage = nullptr;
	}
#pragma endregion

}

测试过程

测试代码:

#include "vector.hpp"
using namespace str;

void Test01() {
	cout << "1号测试机测试开始:" << endl;
	cout << "测试无参构造函数vector()" << endl;
	vector<int> v;
	v.ShowInfo();
	cout << "测试点1通过" << endl;
	cout << "测试赋值符重载vector<T>& operator=(std::initializer_list<T> _Ilist)" << endl;
	v = { 6,3,5,4,9,1 };
	v.ShowInfo();
	cout << "测试点2通过" << endl;
	cout << "测试拷贝构造函数vector(const vector<T>& _Right)" << endl;
	vector<int> v1(v);
	v1.ShowInfo();
	cout << "测试点3通过" << endl;
	cout << "测试参数列表构造函数vector(std::initializer_list<T> _Ilist)" << endl;
	vector<int> v2{ 3,3,3,6,6,6 };
	v2.ShowInfo();
	cout << "测试点4通过" << endl;
	cout << "测试赋值重载vector<T>& operator=(const vector<T>& _Right)" << endl;
	v2 = v;
	v2.ShowInfo();
	cout << "测试点5通过" << endl;
	cout << "测试有参构造函数vector(int num, const T& _Val = T())" << endl;
	vector<int> v3(5, 9);
	v3.ShowInfo();
	cout << "测试点6通过" << endl;

	cout << "-----------------1号测试机全部测试样例通过--------------------------" << endl;
}
void Test02() {
	cout << "2号测试机测试开始:" << endl;
	cout << "测试下标访问符重载 const T& operator[](size_t _Pos)const与const T& at(size_t _Pos)const" << endl;
	vector<int> v{ 0,1, 2, 3, 4, 5, 6 };
	cout << v.at(3) << " " << v[4] << endl;
	cout << "测试点1通过" << endl;
	cout << "测试const T& front()const 与 const T& back()const" << endl;
	cout << v.front() << " " << v.back() << endl;
	cout << "测试点2通过" << endl;
	cout << "-----------------2号测试机全部测试样例通过--------------------------" << endl;
}
void Test03() {
	cout << "3号测试机测试开始:" << endl;
	cout << "测试iterator begin()  与  iterator end()" << endl;
	vector<int> v{ 0,1, 2, 3, 4, 5, 6 };
	cout << v.begin() << " " << v.end() << endl;
	cout << *v.begin() << " " << *(v.end()-1) << endl;
	cout << "测试点1通过" << endl;
	cout << "测试const_iterator cbegin() 与 const_iterator cend()" << endl;
	cout << v.cbegin() << " " << v.cend() << endl;
	cout << *v.cbegin() << " " << *(v.cend()-1) << endl;
	cout << "测试点2通过" << endl;
	cout << "-----------------3号测试机全部测试样例通过--------------------------" << endl;
}
void Test04() {
	cout << "4号测试机测试开始:" << endl;
	cout << "测试push_back(),附带insert(),reserve();测试empty(),size(),capacity()" << endl;
	vector<int> v;
	if (v.empty())cout << "空表,准备插入元素:" << endl;
	for (int i = 0; i < 10; i++)
		v.push_back(i);
	cout << "size=" << v.size() << " capacity=" << v.capacity() << endl;
	v.ShowInfo();
	cout << "-----------------4号测试机全部测试样例通过--------------------------" << endl;
}
void Test05() {
	cout << "5号测试机测试开始:" << endl;
	cout << "测试assign(),pop_back()附带erase(),clear()" << endl;
	vector<int> v{ 1,1,2,3,4,5,6,7,7 };
	v.ShowInfo();
	vector<int> sv;
	sv.assign(v.begin() + 1, v.end() - 1);
	sv.ShowInfo();
	sv.pop_back();
	sv.ShowInfo();
	sv.clear();
	if (sv.empty())cout << "测试通过" << endl;
	cout << "-----------------5号测试机全部测试样例通过--------------------------" << endl;
}
void Test06() {
	cout << "6号测试机测试开始:" << endl;
	cout << "测试void swap(const vector<T>& _Right)" << endl;
	vector<int> v1(10, 5);
	vector<int> v2(5, 10);
	cout<<"v1:"; v1.ShowInfo();
	cout<<"v2:"; v2.ShowInfo();
	cout << "交换后:" << endl;
	v1.swap(v2);
	cout << "v1:"; v1.ShowInfo();
	cout << "v2:"; v2.ShowInfo();
	cout << "-----------------6号测试机全部测试样例通过--------------------------" << endl;
}



int main() {
	Test01();
	Test02();
	Test03();
	Test04();
	Test05();
	Test06();
	return 0;
}

测试结果:


(.hpp)后缀解析

错误:编译->链接阶段出错

1)错误信息:LNK1120(13个无法解析的外部命令);LNK2019(无法解析外部符号)

2)原因分析:vector类为模板类,不可以分成头文件(.h)和源程序文件(.cpp)编程。

3)解决方案:将模板类的定义及成员函数的类外实现的代码写到一个文件中,约定俗成的后缀名为(.hpp),代表模板类头文件。 


感谢大家观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/887372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)

文章目录 设计模式概述1、原型模式2、原型模式的使用场景3、优点4、缺点5、主要角色6、代码示例7、总结题外话关于使用序列化实现深拷贝 设计模式概述 创建型模式&#xff1a;工厂方法、抽象方法、建造者、原型、单例。 结构型模式有&#xff1a;适配器、桥接、组合、装饰器、…

深度学习中的结构化概率模型 - 结构化概率模型的深度学习方法篇

序言 在深度学习的广阔领域中&#xff0c;结构化概率模型&#xff08; Structured Probabilistic Model \text{Structured Probabilistic Model} Structured Probabilistic Model&#xff09;扮演着至关重要的角色。这类模型利用图论中的图结构来表示概率分布中随机变量之间的…

Spring Boot ⽇志

目录 1.⽇志使⽤ 2.⽇志级别 3.⽇志配置 3.1配置⽇志级别 3.2⽇志持久化 3.3配置⽇志⽂件分割 4.更简单的⽇志输出 1.⽇志使⽤ 在使用之前我们先来了解一下为什么要使用&#xff1f; ⽇志的⽤途 1.系统监控 我们可以通过⽇志记录这个系统的运⾏状态&#xff0c;对数…

20款奔驰CLS300升级原厂抬头显示HUD 23P智能辅助驾驶 触摸屏人机交互系统

以下是为您生成的一份关于 18 款奔驰 CLS 老款改新款的改装文案&#xff1a; 18 款奔驰 CLS 老款改新款&#xff1a;科技升级&#xff0c;畅享极致驾驶体验 在汽车改装的世界里&#xff0c;每一次的升级都是对卓越的追求。今天&#xff0c;让我们一同探索 18 款奔驰 CLS 老款改…

在登陆功能中添加Redis缓存

目录 基于Redis实现短信登录 实现流程图 实现代码 解决登录状态刷新问题 初始方案思路&#xff1a; 实现代码 发送验证码 登陆实现 如果是新用户则自动创建 运行测试 基于Redis实现短信登录 实现流程图 实现代码 Overridepublic Result login(LoginFormDTO loginForm…

Thinkphp/Laravel旅游景区预约系统的设计与实现

目录 技术栈和环境说明具体实现截图设计思路关键技术课题的重点和难点&#xff1a;框架介绍数据访问方式PHP核心代码部分展示代码目录结构解析系统测试详细视频演示源码获取 技术栈和环境说明 采用PHP语言开发&#xff0c;开发环境为phpstudy 开发工具notepad并使用MYSQL数据库…

【深度学习】— 多层感知机介绍、 隐藏层、从线性到非线性、线性模型的局限性

【深度学习】— 多层感知机介绍 4.1 多层感知机4.1.1 隐藏层线性模型的局限性引入隐藏层 4.2 从线性到非线性线性组合的局限性引入非线性堆叠更多隐藏层 4.1 多层感知机 在第 3 节中&#xff0c;我们介绍了 softmax 回归&#xff0c;并实现了其从零开始的实现和基于高级 API 的…

如何在dblp官网下载外文文献

文章目录 零、dblp官网一、直接下载pdf二、Zotero 零、dblp官网 ①老域名&#xff1a;https://dblp.uni-trier.de/ ②简化域名&#xff1a;dblp.org 一、直接下载pdf 1.举例&#xff0c;搜索 image matching 2.直接点击红色的PDF按钮 (可能会因为卡&#xff0c;点了没反应。…

No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史

大家好&#xff01;作为一个喜欢探索本质的INTP&#xff0c;我整理了一份简明易懂的Web安全笔记。希望能帮助你轻松掌握这个领域的核心知识。 这份笔记涵盖了Web发展的历程&#xff0c;从静态的Web 1.0到智能化的Web 3.0。我们将探讨URL和HTTP协议&#xff0c;揭示它们在网络中…

24-10-2-读书笔记(二十二)-《契诃夫文集》(一)上([俄] 契诃夫 [译] 汝龙)啊!真想生活。

文章目录 《契诃夫文集》&#xff08;一&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09;早期生活——塔甘罗格&#xff08;人物家庭简介&#xff09;学生时期——莫斯科&#xff08;写作与学习&#xff09;流浪时期——哈萨林&#xff08;游历与流浪&#xff09…

Java中的封装、继承、多态

目录 封装 概念 包 继承 多态 向上转型 一、直接赋值 二、方法传参 三、返回值 向上转型注意事项 向下转型 格式 重写 重写和重载的区别 动态绑定 静态绑定和动态绑定 封装 概念 简单来说就是套壳屏蔽细节。 举例&#xff1a; 想要访问它们时需要一些“接口”…

Java项目实战II基于Java+Spring Boot+MySQL的大创管理系统(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 在当前创新创业氛围浓厚的背景下&#xff0c;大学生创新创业项目&#xff08;简称“大创”&#xff0…

国内旅游:现状与未来趋势分析

在当今社会快速发展的背景下&#xff0c;国内旅游更是呈现出蓬勃的发展态势。中国&#xff0c;这片拥有悠久历史、灿烂文化和壮丽山河的广袤土地&#xff0c;为国内旅游的兴起与发展提供了得天独厚的条件。 本报告将借助 DataEase 强大的数据可视化分析能力&#xff0c;深入剖…

基于SpringBoot的学习资源共享平台

运行环境: jdk8tomcat9mysqlIntelliJ IDEAmavennodejs 设计选用前后端分离的单体架构方式 后端&#xff1a;SpringBootMybatis-PluslogbackElasticsearchRedisMySQLJwtsmtp阿里云OSS 前端&#xff1a;WebPackVueJsAnt Designaxios 主要模块&#xff1a;反馈管理、资源管理、…

【最新】微信小程序连接onenet——stm32+esp8266+onenet实现查看温湿度,控制单片机

微信小程序——stm32esp8266onenet实现查看温湿度&#xff0c;控制单片机 &#xff08;最新已验证&#xff09;stm32 新版 onenet dht11esp8266/01s mqtt物联网上报温湿度和控制单片机(保姆级教程) &#xff1a;↓↓&#x1f447; &#x1f447; &#x1f447; &#x1f447…

unreal engine5制作动作类游戏时,我们使用刀剑等武器攻击怪物或敌方单位时,发现攻击特效、伤害等没有触发

UE5系列文章目录 文章目录 UE5系列文章目录前言一、问题分析二、解决方法1. 添加项目设置碰撞检测通道2.玩家角色碰撞设置3.怪物角色碰撞预设 最终效果 前言 在使用unreal engine5制作动作类游戏时&#xff0c;我们使用刀剑等武器攻击怪物或敌方单位时&#xff0c;发现攻击特效…

(17)MATLAB使用伽马(gamma)分布生成Nakagami-m分布的方法1

文章目录 前言一、使用伽马分布生成Nakagami分布随机变量的方法一二、MATLAB仿真代码后续 前言 MATLAB在R2013a版本中引入Nakagami分布对象&#xff0c;可以用来生成Nakagami随机变量。但是在更早的MATLAB版本中&#xff0c;并没有可以直接生成 Nakagami分布的随机变量的内置的…

51单片机系列-按键检测原理

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 独立按键是检测低电平的。 下面我们来看一张对应的电路原理图&#xff1a; 在这张图当中&#xff0c;P1&#xff0c;P2&#xff0c;P3内部都上拉了电阻&#xff0c;但是P0没有&am…

一款基于.NET开发的简易高效的文件转换器

前言 今天大姚给大家分享一款基于.NET开发的免费&#xff08;GPL-3.0 license&#xff09;、简易、高效的文件转换器&#xff0c;允许用户通过Windows资源管理器的上下文菜单来转换和压缩一个或多个文件&#xff1a;FileConverter。 使用技术栈 ffmpeg&#xff1a;作为文件转换…

知识图谱入门——10:使用 spaCy 进行命名实体识别(NER)的进阶应用:基于词袋的实体识别与知识抽取

在构建知识图谱的过程中&#xff0c;如何准确地识别和提取实体是关键。spaCy 提供了强大的命名实体识别&#xff08;NER&#xff09;功能&#xff0c;我们可以结合自定义规则和工具来实现更精准的实体抽取。本文将详细探讨如何在 spaCy 中实现自定义实体抽取&#xff0c;包括使…