目录
一. vector成员变量
二. vector的构造函数和析构函数
三. vector的成员函数
1. 容量操作函数
扩容操作
(1). finish更新问题
(2). 扩容深浅拷贝问题
resize与尾插、尾删与判空
insert与erase与clear
2. 函数重载
(1). 赋值运算符重载
(2). [ ]重载进行访问
四. 完整代码
vector的底层实现主要依赖于动态数组,特点是可以根据需要自动扩展和收缩
我们在这里将其封装到一个命名空间中来实现
一. vector成员变量
vector成员变量是三个迭代器,这里我们用指针typedef实现
#include<iostream>
#include<assert.h>
#include<cstring>
using namespace std;
namespace Pc
{template<typename T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//......private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
我们再实现begin()和end()来返回迭代器(代码量小的都是在类内定义的)
iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}
二. vector的构造函数和析构函数
分别是默认构造函数,用迭代器构造,还有构造将n个val给予数组以及拷贝构造函数
vector()//什么也不写,成员变量走了初始化列表{}//vector() = default;//关键字,强制生成构造函数template<typename InputIterator>//这样就可以用任意的迭代器初始化,要求类型是匹配的vector(InputIterator first,InputIterator last){while (first!=last){push_back(*first);first++;}}vector(size_t n, const T& val = T()){reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(const vector<T>& v){reserve(size());for (auto& e : v){push_back(e);//直接尾插即可}}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}
const T& val = T(),给缺省值使自定义类型走构造函数,内置类型int等也有默认构造的概念(当然也有析构)
如下代码
int i = int(); //内置类型int等也有默认构造的概念,当然也有析构int j = int(1);int k(2);
为什么使用迭代器进行构造使用模版函数呢,因为这样就可以使用任意类型的迭代器初始化,要求类型匹配,但这样也有问题
例如以下测试用例
void test_vector5(){vector<int> v1;v1.push_back(1);v1.push_back(1);v1.push_back(1);v1.push_back(2);v1.push_back(2);v1.push_back(2);vector<int> v2(v1.begin() , v1.end());vector_printf(v1);vector_printf(v2);list<int> lt;lt.push_back(10);lt.push_back(10);lt.push_back(10);lt.push_back(10);vector<int> v3(lt.begin(), lt.end());vector<string> v4(10, "11111");vector<string> v5(10, "");vector_printf(v3);vector_printf(v4);vector_printf(v5);vector<int> v6(10, 1);//因为两个参数一样会调用vector(InputIterator first,InputIterator last)//而非 vector(size_t n, const T& val = T())//解决方法1. 将第一个参数强转,2. 将函数重载第一个参数为int类型 vector_printf(v6);}
因为两个参数一样会调用vector(InputIterator first,InputIterator last)而非 vector(size_t n, const T& val = T())
此时我们我们可以
1. 将第一个参数强转
2. 将函数重载第一个参数为int类型,即我们上面的代码
三. vector的成员函数
1. 容量操作函数
扩容操作
size_t size() const//求元素个数{return _finish - _start;}size_t capacity() const//求vector容量{return _end_of_storage - _start;}void reserve(size_t n){if (n > capacity()){size_t old_size = _finish - _start;T* tmp = new T[n];//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝,string之类的释放就为随机值了for (size_t i = 0; i < old_size; i++){tmp[i]= _start[i] ; }delete[] _start;_start = tmp;_finish = old_size + _start;_end_of_storage = _start + n;}}
(1). finish更新问题
在开辟新空间后原有的空间就释放了所以我们不可以在释放空间后将_finish的值更新写为
_finish=tmp+size();
因为size()的实现是要用到已经被释放的_finish的,我们可以
1. 将_finish的更改写到delete[] 即释放空间前面
2. 用一个数记录size(),使_finish再空间释放后加这个数
(2). 扩容深浅拷贝问题
这里我们为什么不用memcpy呢?
1. memcpy是内存的二进制格式拷贝,将一段内存空间中的内容原封不动的拷贝到另外一段内存空间中
2. . 如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型 元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
比如存储的是string或者是vector,进行了浅拷贝后就被释放了,那相当于存储了几个被释放的空间
如果对象中涉及到资源管理时,一定不能用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃
resize与尾插、尾删与判空
//自定义类型会调用默认构造,为了兼容 内置类型int等也有默认构造的概念void resize(size_t n, T val = T())//const T& val=T(){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;_finish++;}}}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}void pop_back(){assert(!empty());_finish--;}bool empty(){return _start == _finish;}
上面是resize是调整容器大小使其包含n个元素,
n<容器大小则内容将减少到其前 n 个元素,并删除超出其范围的元素(并销毁它们)。
n 大于当前容器大小,则通过在末尾插入尽可能多的元素来扩展内容,以达到 n 的大小
大于容器大小会扩容到指定大小
剩下的为尾插尾删和判断vector是否为空
insert与erase与clear
void insert(iterator pos, const T& x){assert(pos - _start <= size());if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}iterator end = _finish;while (end > pos){*end = *(end - 1);end--;}*end = x;_finish++;}void erase(iterator pos){assert(pos - _finish < 0);iterator end = pos;while (end != _finish){*end = *(end + 1);end++;}_finish--;}void clear(){_finish = _start;}
需要注意的还是迭代器失效问题,如果使用迭代器需要在扩容操作后及时更新
2. 函数重载
(1). 赋值运算符重载
//vector<T>& operator=(const vector<T>& v)//{// if (this != &v)//若,地址相同说明是自己给自己赋值,什么也不用干// {// clear();// reserve(v.size());// for (auto& e : v)// {// push_back(e);// }// }// //if (this != &v)// //{// // vector<T> tmp(v);// // swap(tmp);// //}// return *this;//}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//vector& operator=(vector v)//类内允许这样,类外不可以vector<T>& operator=(vector<T> v){swap(v);return *this;}
我们有两种方法三种方法
1. 参数不传引用通过自己实现的交换函数,与v这个拷贝构造的对象交换,v在函数结束生命周期就结束了
2. 参数传引用判断赋值的是不是自己,是就直接返回,不是就清空vector后一个个尾插
3. 参数传引用,手动拷贝构造一个vector类对象与自身交换
(2). [ ]重载进行访问
实现可读可写与可读不可写两种,返回引用减少拷贝消耗
T& operator[](size_t pos){assert(pos < size());return _start[pos];}T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}
四. 完整代码
#include<iostream>
#include<assert.h>
#include<cstring>
#include<list>
using namespace std;
namespace Pc
{template<typename T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector()//什么也不写,成员变量走了初始化列表{}//vector() = default;//关键字,强制生成构造函数template<typename InputIterator>//这样就可以用任意的迭代器初始化,要求类型是匹配的vector(InputIterator first,InputIterator last){while (first!=last){push_back(*first);first++;}}vector(size_t n, const T& val = T()){reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){//reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(const vector<T>& v){//reserve(size());for (auto& e : v){push_back(e);//直接尾插即可}}//vector<T>& operator=(const vector<T>& v)//{// if (this != &v)//若,地址相同说明是自己给自己赋值,什么也不用干// {// clear();// reserve(v.size());// for (auto& e : v)// {// push_back(e);// }// }// //if (this != &v)// //{// // vector<T> tmp(v);// // swap(tmp);// //}// return *this;//}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//vector& operator=(vector v)//类内允许这样,类外不可以vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}void reserve(size_t n){if (n > capacity()){size_t old_size = _finish - _start;T* tmp = new T[n];//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝,string之类的释放就为随机值了for (size_t i = 0; i < old_size; i++){tmp[i]= _start[i] ; }delete[] _start;_start = tmp;_finish = old_size + _start;_end_of_storage = _start + n;}}bool empty(){return _start == _finish;}//自定义类型会调用默认构造,为了兼容 内置类型int等也有默认构造的概念void resize(size_t n, T val = T())//const T& val=T(){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;_finish++;}}}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}void pop_back(){assert(!empty());_finish--;}void insert(iterator pos, const T& x){assert(pos - _start <= size());if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}iterator end = _finish;while (end > pos){*end = *(end - 1);end--;}*end = x;_finish++;}void erase(iterator pos){assert(pos - _finish < 0);iterator end = pos;while (end != _finish){*end = *(end + 1);end++;}_finish--;}void clear(){_finish = _start;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
这篇就到这里啦
(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤