【C++】简化源码——vector的模拟实现
创始人
2024-02-19 12:55:57

文章目录

    • 一、前言
    • 二、无参构造&析构
    • 三、基础接口
      • 1.empty和clear
      • 2.size和capacity
      • 3.[]和iterator
    • 四、resize和reserve
    • 五、尾插尾删
    • 六、其他构造
    • 七、迭代器失效
      • 1.insert
      • 2.erase
    • 八、memcpy问题
    • 九、vector.h

一、前言

本篇的目的很简单,只有一个:模拟实现vector

如何去模拟实现?我们可以看看vector的源码,我们可以抽离出主体框架:

template 
class vector {typedef T value_type;typedef value_type* iterator;typedef const value_type* const_iterator;
protected:iterator start;iterator finish;iterator end_of_storage;
}

这本质上与T*a,size_t size,size_t capacity是类似的:

image-20221125100737422

对于size = _finish - _start

对于capacity = _endofstorage-_start

有了这些作为铺垫,我们对于vector的模拟实现大概有了一个基本的框架,话不多说,直接进入主题👇


二、无参构造&析构

对于这两个都是我们熟悉的老朋友了

  • 无参构造

初始化全设置为空:

vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{}
  • 析构
~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}

三、基础接口

1.empty和clear

empty

bool empty() const
{return _finish == _start;
}

clear

void clear()
{_finish = _start;//这里可不是置为nullptr哦
}

2.size和capacity

size

size_t size() const
{return _finish - _start;
}

capacity

size_t capacity() const 
{return _endofstorage - _start;
}

3.[]和iterator

[]

提供const版本和非const版本:

T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos)const
{assert(pos < size());return _start[pos];
}

iterator

同理普通迭代器和const迭代器版本,同理,范围for循环此时也是可以实现的:

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;}

四、resize和reserve

这两个接口需要单独拎出来,这是因为后面的插入等相关操作需要用到,所以我们先来看看这两个接口,同时这里有一些问题值得我们去注意:

resize

n个数据去初始化,这个n是多大,会造成什么影响?我们需要进行分类讨论:

image-20221126223553778

//分情况
void resize(size_t n, const T& val = T())
{if (n > capacity()){reserve(n);}if (n > size()){while (_finish < _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}
}

resize的参数初始化值为T类型的构造,这里可不能直接初始化为0,要是T是自定义类型呢?是vector呢?所以这里如果T是vector的化调用的就是vector的构造函数。另外,这里还需要注意的一点是:构造vector的时候是匿名对象,匿名对象具有常性,不可修改所以要加上const修饰

所以,我们自然而然可以知道,对于内置类型比如int,都是有构造函数的:

image-20221126225324427

reserve

reserve最大的问题就是深拷贝!开辟新空间进行赋值的时候如果直接使用memcpy是浅拷贝

void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];//size()需要先保存起来,后面_start会发生改变size_t sz = size();//为空不需要拷贝了if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝//delete[] _start;}_start = tmp;_finish = _start+sz;_endofstorage = _start + n;}
}
  • size()需要先用sz保存

为啥?如果没有保存:

_start = tmp;
_finish = _start+size();
_endofstorge = _start+n;

不要忘了:size()=_finish-_start, 而_start = tmp会更新_start;根本就不是原来的size()了

  • 使用memcpy问题

memcpy拷贝数据,拿vector作为例子,其中vector仍然是浅拷贝的,对于自定义类型出现问题

vector> vv;
vector v(4, 1);
//复用push_back尾插
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
//需要扩容成2倍
vv.push_back(v);
for (size_t i = 0; i < vv.size(); i++)
{
for (size_t j = 0; j < vv[i].size(); j++)
{cout << vv[i][j] << " ";
}
cout << endl;
}

image-20221127001111216


五、尾插尾删

void push_back(const T& x)
{if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish = x;++_finish;
}void pop_back()
{assert(_finish > _start);--_finish;
}

六、其他构造

这里需要复用前面的一些接口,所以放在这个地方

  • 迭代器区间构造

这里复用了push_back,而且写成了模板

template //模板可以使用其他迭代器区间
vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);//int不能解引用++first;}}

类模板的成员函数可以是函数模板,使之可以是任意类型的迭代器区间,包括了自身的迭代器区间构造

另外,初始化列表全部初始化为nullptr,没有初始化就是随机值,出现野指针

  • 拷贝构造

初始化列表全都要初始化为nullptr,否则就是随机值

//写法1
vector(const vector& v):_start(nullptr),_finish(nullptr),_endofstorage(nullptr){reserve(v.capacity());for (const auto& e : v){push_back(e);}}
//写法2
void swap(vector& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}vector(const vector& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){vector tmp(v.begin(), v.end());swap(tmp);}
  • 赋值重载

赋值重载需要复用拷贝构造

	//缺陷:自己拷贝自己
//v1(v2)
vector& operator=(vector v)
{swap(v);return *this;
}

这种写法就是有一个小问题:如果是自己拷贝自己呢?加个判断?没用,因为此时已经传值传参过来了,加个判断没啥意义了。但是这个问题不大,我们允许存在,平时自己也很少自己赋值自己。

另外,这里是传值调用,有人会说了:传引用也可以啊,此时如果是引用的话,v2赋值给v1,v1不是v2的拷贝,直接把v2换成了v1,v1换给了v2,v2本身已经发生变化了,这不是赋值了。


七、迭代器失效

1.insert

insert这个太熟悉了,废话不多说直接上手代码:

//迭代器失效:扩容引起野指针问题
void insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;
}

测试代码:

void Test3()
{vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;vector::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto e : v){cout << e << " ";}cout << endl;
}

image-20221127102831809

这是因为扩容导致pos失效了:

image-20221127103607613

insert过程中发生扩容,导致it指向的空间实际上已经被释放,it指向已被释放的空间是野指针,造成了迭代器失效

所以,我们应该去更新pos,算出pos刚开始的相对位置,然后再去进行更新即可解决问题。但是此时外面调用insert的it仍然是失效的,因为是传值调用,形参改变不影响实参,可以通过返回值接收解决问题。(如果是传引用的话,只能传变量,而临时对象具有常性,不能调用,存在很多问题),所以直接用返回值解决。

iterator insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){//扩容会导致pos迭代器失效,需要更新size_t len = pos - _start;size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;
}

测试代码:

void Test3()
{vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;v.insert(v.begin(), 0);vector::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto e : v){cout << e << " ";}cout << endl;
}

image-20221127110313361

2.erase

挪动数据进行覆盖即可

void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;
}

erase的pos也可能会导致pos失效,测试代码:

void Test6(){//删除所有偶数vector v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);vector::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){v.erase(it);}++it;}for (auto e : v){cout << e << " ";}cout << endl;}

测试发现:

1,2,3,4的时候发生崩溃

1,2,2,3,5结果只删了一个2

1,2,3,4,5结果是正常的

image-20221127140628122

上述代码在VS下,当erase(it)之后,it指向的位置发生改变,然后在++it的话,会出现问题,出现一些错误,造成迭代器失效。

我们最好统一认为失效了。

正确的erase:

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;
}		

测试代码:

void Test6(){//删除所有偶数vector v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);vector::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}

image-20221127121509374

对于insert和erase存在迭代器失效的问题,当迭代器失效而来,我们就不要再去访问pos的位置了,要更新pos的位置,可通过返回值接收进行访问,


八、memcpy问题

如果拷贝的是内置类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝,指向同一块空间,假设我们仍然在reserve接口中使用memcpy进行拷贝:

我们以vector类为例子:

void Test10()
{vector v;v.push_back("11111111111111111111");v.push_back("22222222222222222222");v.push_back("33333333333333333333");v.push_back("44444444444444444444");v.push_back("55555555555555555555");for (auto e : v){cout << e << " ";}cout << endl;
}

image-20221127143935504

image-20221127145226373

所以我们要调用自己的拷贝,一个一个进行深拷贝。


九、vector.h

#define _CRT_SECURE_NO_WARNINGS#pragma oncenamespace hwc
{templateclass 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;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}/*vector(const vector& v):_start(nullptr),_finish(nullptr),_endofstorage(nullptr){reserve(v.capacity());for (const auto& e : v){push_back(e);}}*///vector v1(10, 5);//vector v2(10, 'A');vector(size_t n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}//改成int或强转vector(int n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}template vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);//int不能解引用++first;}}vector(const vector& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){vector tmp(v.begin(), v.end());swap(tmp);}//缺陷:自己拷贝自己vector& operator=(vector v){swap(v);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t sz = size();//为空不需要拷贝了if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝//delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}//分情况void resize(size_t n, const T& val = T()){if (n > capacity()){reserve(n);}if (n > size()){while (_finish < _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}}bool empty() const{return _finish == _start;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void push_back(const T& x){if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish = x;++_finish;}void pop_back(){assert(_finish > _start);--_finish;}//迭代器失效:野指针问题/*void insert(iterator pos, const T& val){assert(pos >= _start);assert(pos < _finish);if (_finish == _endofstorge){reserve(capacity() == 0 ? 4 : capacity() * 2);}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;}*/iterator insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){//扩容会导致pos迭代器失效,需要更新size_t len = pos - _start;size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}void swap(vector& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}void clear(){_finish = _start;}public:iterator _start;iterator _finish;iterator _endofstorage;};void Test1(){vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cout << v.size() << endl;cout << v.capacity() << endl;for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;vector::iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;}void Test2(){vector v;v.resize(10, -1);for (auto e : v){cout << e << " ";}cout << endl;v.resize(5);for (auto e : v){cout << e << " ";}cout << endl;v.pop_back();v.pop_back();v.pop_back();v.pop_back();v.pop_back();v.pop_back();}void Test3(){vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;v.insert(v.end(), 0);for (auto e : v){cout << e << " ";}cout << endl;vector::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto e : v){cout << e << " ";}cout << endl;}void Test4(){vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){//传值v.insert(it, 30);}//insert以后it不能使用,可能迭代器失效(野指针)//(*it)++;for (auto e : v){cout << e << " ";}cout << endl;}void Test5(){vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.erase(it);}cout << *it << endl;//	(*it)++;for (auto e : v){cout << e << " ";}cout << endl;}void Test6(){//删除所有偶数vector v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}void Test7(){vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector v1(v);for (auto e : v1){cout << e << " ";}cout << endl;vector v2;v2.push_back(10);v2.push_back(20);v2.push_back(30);v1 = v2;for (auto e : v1){cout << e << " ";}cout << endl;v1 = v1;for (auto e : v1){cout << e << " ";}cout << endl;}void Test8(){string str("hello world");vector v(str.begin(), str.end());for (auto e : v){cout << e << " ";}cout << endl;/*vector v1(v.begin(), v.end());*/vector v1(10, 5);//vector v2(10, 'A');for (auto e : v1){cout << e << " ";}cout << endl;}class Solution {public:vector> generate(int numRows) {vector> vv;vv.resize(numRows);for (size_t i = 0; i < vv.size(); i++){vv[i].resize(i + 1, 0);vv[i][0] = vv[i][vv[i].size() - 1] = 1;}for (size_t i = 0; i < vv.size(); i++){for (size_t j = 0; j < vv[i].size(); j++){if (vv[i][j] == 0){vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];}}}return vv;}};void Test9(){vector> vvRet = Solution().generate(5);for (size_t i = 0; i < vvRet.size(); i++){for (size_t j = 0; j < vvRet[i].size(); j++){cout << vvRet[i][j] << " ";}cout << endl;}/*vector> vv;vector v(10, 1);vv.push_back(v);vv.push_back(v);vv.push_back(v);vv.push_back(v);vv.push_back(v);for (size_t i = 0; i < vv.size(); i++){for (size_t j = 0; j < vv[i].size(); j++){cout << vv[i][j] << " ";}cout << endl;}*/cout << endl;}void Test10(){vector v;v.push_back("11111111111111111111");v.push_back("22222222222222222222");v.push_back("33333333333333333333");v.push_back("44444444444444444444");v.push_back("55555555555555555555");for (auto e : v){cout << e << " ";}cout << endl;}
}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...