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_iteratorprotected: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个空间,并全部赋值为_Valvector(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);//扩sizevoid 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 functionsvector() :_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 Iteratorsiterator 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 Capacitybool empty()const;size_t size()const;size_t capacity()const;void resize(size_t _Newsize);//扩sizevoid reserve(size_t _Newcapacity);//扩capacity//void shrink_to_fit();
#pragma endregion
#pragma region Midifiervoid 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 endregionvoid 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//Capccitytemplate<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 accesstemplate<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);}/*----------------------------------------------------------------------------*///Iteratorstemplate<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;}/*----------------------------------------------------------------------------*///Modifiertemplate<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 functionstemplate<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),代表模板类头文件。
感谢大家观看。
相关文章:

C++模拟实现vector容器【万字模拟✨】
更多精彩内容..... 🎉❤️播主の主页✨😘 Stark、-CSDN博客 本文所在专栏: 学习专栏C语言_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客 数据结构与算法_Stark、的博客-CSDN博客 座右铭:梦想是一盏明灯ÿ…...

论文笔记:LAFF 文本到视频检索的新基准
整理了ECCV2022 Lightweight Attentional Feature Fusion: A New Baseline for Text-to-Video Retrieval 论文的阅读笔记 背景模型问题定义LAFF(Lightweight Attention Feature Fusion)LAFF Block 实验消融实验可视化对比试验 这篇文章提出了一种新颖灵活的特征融合方式&#x…...

iSTFT 完美重构的条件详解
目录 引言1. 短时傅里叶变换(STFT)与逆变换(iSTFT)概述2. 完美重构的条件3. 数学推导4. 实现要点5. 示例代码6. 总结 引言 在数字信号处理领域,短时傅里叶变换(Short-Time Fourier Transform,简…...
SSH(安全外壳协议)可以基于多种加密算法
SSH(安全外壳协议)可以基于多种加密算法,确保数据的机密性和完整性。以下是 SSH 中常见的加密类型: 1. 对称加密 对称加密算法用于加密会话中的数据,常见的算法包括: AES(高级加密标准&#…...

Navicat 工具 下载安装
准备工作 下载 下载链接:https://www.123865.com/ps/EF7OTd-kdAnH 演示环境 操作系统:windows10 产品:Navicat 版本: 15.0.25 注意:如果需要其他版本可以自行下载。 安装步骤 1、解压(如果解压中出现提示…...

家用高清投影仪怎么选?目前口碑最好的投影仪推荐
双十一马上要到了,而且今年还有投影仪的家电国补,所以大家入手投影仪的需求也越来越多,但是家用高清投影仪怎么选?什么投影仪最适合家用?家庭投影仪哪个牌子质量最好?今天就给大家做一个2024性价比高的家用…...
阿里云盾同步漏洞之限制请求数
阿里云sdk不支持一次性请求太多,所以我们需要限制每次请求最大1000条,此代码无任何参考意义。仅做记录 func VulList(hole_type string) ([]*sas20181203.DescribeVulListResponseBodyVulRecords, error) {pageSize : 20allItems : make([]*sas20181203…...

docker安装kafka-manager
kafkamanager docker安装_mob64ca12d80f3a的技术博客_51CTO博客 # 1、拉取镜像及创建容器 docker pull hlebalbau/kafka-manager docker run -d --name kafka-manager -p 9000:9000 --networkhost hlebalbau/kafka-manager# 2、增设端口 腾讯云# 3、修改防火墙 sudo firewall-…...

Android Studio 新版本 Logcat 的使用详解
点击进入官方Logcat介绍 一个好的Android程序员要会使用AndroidStudio自带的Logcat查看日志,会Log定位也是查找程序bug的第一关键。同时Logcat是一个查看和处理日志消息的工具,它可以更快的帮助开发者调试应用程序。 步入正题,看图说话。 点…...

基于php摄影门户网站
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…...

uniapp中uni.request的统一封装 (ts版)
文章目录 前言一、我们为什么要去封装?二、具体实现1.创建一个请求封装文件:2.封装 uni.request:3.如何去使用? 总结 前言 在uniapp中如何去更简洁高效的发送我们的请求,下面就介绍了uni.request()二次封装。 一、我们…...
记录一次gRpc流式操作(jedis版)
使用背景: 从redis队列中发送和消费消息.(使用gRpc的流式实现的消费消息) gRpc协议类定义 service方法定义 service MQDataService{ rpc sendFacebookAndroidMsg(google.protobuf.StringValue)returns (ResultProto); rpc receiveFacebookAndroidMsg(empty)returns (stream g…...

20241001国庆学习
n60f/p 这个n是指旋转磁场的速度。 极数表示旋转转子的永磁体极数,具有一对N极/S极的电机称为双极电机。 极数可以是2、4、6、8等。 (从电机控制的角度来看,当极数增加一倍时,转速将减半,当极数增加四倍时…...

基于SSM的农产品仓库管理系统【附源码】
基于SSM的农产品仓库管理系统(源码L文说明文档) 目录 4 系统设计 4.1 系统概要设计 4.2 系统功能结构设计 4.3 数据库设计 4.3.1 数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1 管理员功能介绍 5.1.1 用户管…...

fmt:C++ 格式化库
fmt 是一个现代化、快速且安全的 C 格式化库,专注于高效地格式化文本。它提供了类似 Python 的 format 功能,但具有更高的性能和类型安全特性。fmt 库在处理字符串格式化、日志输出以及构建用户友好的输出时尤为强大。自从 C20 标准引入 std::format 后&…...
RabbitMQ MQ的可靠性及消费者的可靠性
1.MQ可靠性: 如何保证消息的可靠性: (1).通过配置可以让交换机、队列、以及发送的消息都持久化。这样队列中的消息会持久化到磁盘,MQ重起消息依然存在。 (2).3.6.0版本开始,RabbitMQ引入了惰性队列模式,这种模式下&am…...
使用 Nexus 代理 Docker Hub 的配置指南
在本篇文章中,我们将详细介绍如何配置 Nexus 以代理 Docker Hub,从而实现更高效的镜像管理。以下步骤涵盖了从 Nexus 的安装到 Docker 客户端的配置。 1. 配置 Nexus 1.1 登录 Nexus 打开浏览器,访问 Nexus 的 URL(例如 http:/…...

笔记整理—linux进程部分(4)进程状态与守护进程
进程的几种重要状态,就绪态;运行态;僵尸态;等待态(浅度睡眠、深度睡眠);停止态。 就单核CPU而言,在同一时间只能运行一个进程,但实际上要运行的进程不止一个,…...

# VirtualBox中安装的CentOS 6.5网络设置为NAT模式时,怎么使用SecureCRT连接CentOS6.5系统?
VirtualBox中安装的CentOS 6.5网络设置为NAT模式时,怎么使用SecureCRT连接CentOS6.5系统? 一、查询 【VirtualBox Host-Only Network】虚拟网卡的网络配置 IP。 1、按键盘上WIN R 组合键,打开【运行】,输入【 ncpa.cpl 】&…...
7-1.Android SQLite 之 SQLiteDatabase 简单编码模板(SQLiteDatabase 使用、SQL 语句编写)
一、SQLiteDatabase SQLite 是一种轻量级的数据库引擎,它非常适合在移动设备(例如,Android)上使用 SQLiteDatabase 允许应用程序与 SQLite 数据库进行交互,它提供了增删改查等一系列方法 二、SQLiteDatabase 简单编码…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...