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 简单编码…...
灰度图像重心(质心)求取算法
1、图像的重心坐标计算 假设我们有一个二维图像,其中 (x, y) 表示图像中每个像素的坐标。I(x, y) 表示图像在 (x, y) 处的亮度(或像素值),通常是灰度值。 图像的重心坐标 (X, Y) 可以通过以下公式计算: X = Σ [x * I(x, y)] / Σ I(x, y) Y = Σ [y * I(x, y)] / Σ I(…...
k8s 1.28.2 集群部署 ingress 1.11.1 包含 admission-webhook
文章目录 [toc]证书创建部署 ingress-controlleringress 验证创建测试 nginx pod创建错误的 ingress 配置创建正确的 ingress 配置 ingress 官方 yaml 文件:deploy.yaml基于官方 yaml 文件做了一些修改 官方的 svc 是 ClusterIP 和 LoadBalancer,我这边把…...
pom web 自动化测试框架分享
这是初版的 pom web 测试框架,目录如下同时部分代码也放在下面,详细代码可前往 github 查看,欢迎大家给出宝贵意见。 |--base | base_page.py(封装方法) | |--config | allure_config.py(测试报告配…...
一些以前使用的linux及shell命令,gnuplot脚本
tar tar -cvzf xxx.tar.gz * -c,--create 创建新的tar文件 -v,--verbose 列出每一步处理涉及的文件的信息,只用一个“v”时,仅列出文件名 使用两个“v”时,列出权限、所有者、大小、时间、文件名等信息 -z,…...
Django一分钟:DRF模型序列化器处理关联关系的示例与注意事项
DRF的ModelSerializer序列化器与Django的Model模型紧密映射,本文将通过简单的示例介绍几种处理关联关系的方法。 1. 创建模型和初始数据 创建模型 from django.db import modelsclass Product(models.Model):product_name models.CharField(max_length255)quant…...
Python爬虫selenium框架基本使用
一、安装导入 使用包管理器安装 pip3 install selenium 二、WebDriver工具 要使用这个工具我们需要保证安装了一个浏览器的驱动器。 Python的WebDriver是一个用于自动化Web浏览器操作的工具,它属于Selenium的一部分,特别是Selenium 2.0及以后版本中…...
sql 时间交集
任务(取时间交集) 前端输入开始时间和结束时间,通过sql筛选出活动开始时间和活动结束时间再开时时间和结束时间有交集的活动 想法: 前后一段时间内遇到了类似取交集的,从网上找到了两种写法,再结合GPT等…...
【深度学习】05-Rnn循环神经网络-01- 自然语言处理概述/词嵌入层/循环网络/文本生成案例精讲
循环神经网络(RNN)主要用于自然语言处理的。 循环神经网络(RNN)、卷积神经网络(CNN)和全连接神经网络(FCN)是三种常见的神经网络类型,各自擅长处理不同类型的数据。下面…...
基于JAVA+SpringBoot+Vue的电商平台的设计与实现
基于JAVASpringBootVue的电商平台的设计与实现 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末附源码下载链接🍅…...
CSS盒模型-怪异盒模型笔记-思维导图-案例等
文章目录 一、盒模型(重点)二、怪异盒模型三、块级元素和行内元素区别汇总四、块级元素和行内元素的转换(显示方式)||元素的显示和隐藏五、思维导图六、笔记资料 一、盒模型(重点) 所有HTML元素可以看作盒子。 CSS盒模型本质上是…...
专业网站定制 北京/百度竞价排名广告定价鲜花
1. 前言 随着信息技术和互联网的发展, 我们已经步入了一个信息过载的时代,这个时代,无论是信息消费者还是信息生产者都遇到了很大的挑战: 信息消费者:如何从大量的信息中找到自己感兴趣的信息?信息生产者…...
岐山县住房和城市建设局网站/婚恋网站排名前十名
第4章 并发编程 4.5 channel channel的传递 在Go语言中channel本身也是一个原生类型,与map之类的类型地位一样,因此channel本身在定义后也可以通过channel来传递。 利用channel的这个可传递特性,我们可以实现非常强大、灵活的系统架构。相比…...
卡片式主题wordpress/目前推广平台都有哪些
阿里云函数 实现企业微信消息 回调地址验证 getRawBody(req, async function (err, body) {// resp.send(Hello)// 企业微信消息回调地验证if (req.method GET) {var sVerifyMsgSig req.queries.msg_signature;var sVerifyTimeStamp req.queries.timestamp;var sVerifyNonce…...
做企业的网站都要准备什么/爱站网关键词工具
//public——说明方法main()是公有方法,它可以被任何方法访问,包括java解释器。实际上,main()方法只被java解释器调用,其他方法一般不调用它。//static——告诉编译器main()方法是静态的,可用在类Demo中,不…...
网站建设 石景山/大数据分析培训机构
nginx的location配置 使用的版本是nginx1.18.0。 nginx各个版本差别不大,可以通用。 简介 nginx的location配置是为了让不同的url访问指向不同的资源文件位置,例如下面的配置: server {listen 8080;server_name localhost;#chars…...
个人网站后台管理/深圳推广服务
摘要: 如果想要建立一个可伸缩的高可靠性的网站,就需要了解集群技术(clustering).本文中,Abraham Kang介绍了J2EE集群,怎样实现集群, 并列出Bluestone Total-e-server, Sybase Enterprise Application Server, SilverStream Application Server 和WebLogic Application Server在…...