小工具知识网

当前位置: 首页 > 行业知识 > C++:Vector的模拟实现

行业知识

C++:Vector的模拟实现

2026-11-04 13:06:26 来源:互联网转载

��                                                  创作不易,感谢三连 !!

一,前言 

       在学习string类的时候,我们可能会发现遍历的话下标访问特别香,比迭代器用的舒服,但是下标其实只能是支持连续的空间,他的使用是非常具有局限性的,随着STL学习的深入我们会发现其实迭代器才是大佬!!Vector虽然也支持下标访问,但是很多成员函数都是用的迭代器,所以我们要模拟实现的话迭代器十分重要,vs使用的是PJ版的STL版本,比较难懂,所以我们模拟实现统一用SGI版本去实现,所以在模拟实现之前,我们要去看看他的源码到底有哪些成员变量

       SGI下的vector有三个成员变量,通过观察其他源码可以大致推断  _start是指向起始位置,_finish是指向有效数据的下一个位置(迭代器都遵循左闭右开),end_of_storage是指向有效容量的最后一个位置。

     通过这个我们可以观察到SGI版本下的迭代器其实就是一个原生指针,value_type*类型相当于是模板T对应的指针类型,有了这些大致了解,我们就可以去模拟实现啦!!

二,vector的模拟实现

    大致框架需要有模板(类外定义)/迭代器以及迭代器的获取(public定义,要有可读可写的也要有可读不可写的)/成员变量(private定义)  并且为了不和库的vector冲突,我们需要自己搞一个命名空间

namespace cyx{//模板template//迭代器(可读可写)class vector{public:typedef T* iterator;iterator begin(){return _start;}iterator end(){return _finish;}//迭代器(可读不可写)typedef const T* const_iterator;const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}private://成员变量iterator _start;iterator _finish;iterator _end_of_storage;}}

然后我们开始实现!! 

2.1 构造函数和析构函数

2.1.1 无参构造函数

//无参构造函数vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}

2.1.2 迭代器区间构造

//传别人的迭代器进行构造templatevector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){     //这里传的是别人的迭代器,不知道会传多少数据,不能提前扩容,只能让pushback的时候去判断while (first != last){push_back(*first);++first;}}

 push_back是尾插数据,具体实现后面会写。

思考:为什么迭代器也要搞个类模板呢?

        答:本质上是为了让这个函数更加灵活,可以传不同类型的迭代器来帮助我们初始化!!

比如这个地方我们传string类的迭代器

 传vector类的迭代器

 2.1.3 有参构造函数(对n个存储的类型去调用他们的构造)

//有参构造函数(对n个存储的类型去调用他们的构造)vector(size_t n,const T&val=T() ):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);//因为我们知道会进多少数据,所以可以提前开空间for (int i = 0; i  

reserve是扩容到n,具体实现后面会写。

思考:

1.缺省值T( )是什么意思

      答:这个地方的缺省值不能给0!!因为vector可能会存储内置类型,也可能会存储自定义类型,比如vector,所以如果我们没给值,缺省值就要给他的默认无参构造函数,这个默认构造函数可以使用匿名对象。

2.const T&val=T()  T()不是用一次就析构吗,为什么可以用引用

     答:T()是一个用一次就析构的匿名对象,其实本质上是因为他没有名字,用T引用val可以充当他的名字,此时用val就相当于用这个匿名对象,所以匿名对象的生命周期被延长到和val一样,但是由于匿名对象是一个临时变量,所以具有常性,所以必须用const修饰的val才可以当他的别名,否则会出现权限放大!!

3.非法的间接寻址是为什么?

如下图我传(10,5),会出非法间接寻址

 但是我传(10u,5)就可以正常使用了,为什么会这样??

  答:

根据上图写出一个重载的有参构造

//重载一个防止间接寻址vector(int n, const T val = T()):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);//因为我们知道会进多少数据,所以可以提前开空间for (int i = 0; i  

 2.1.4 拷贝构造+memcpy拷贝问题+赋值重载

 但是真的有这么顺利吗??

思考:

为什么存string类就会崩了??    这就涉及到memcpy的拷贝问题

 我们以上述问题来画图解释一下

总结:

1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中

2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

       如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是

浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

 

所以在这个地方我们的拷贝构造不能用memcpy

//拷贝构造(传统)vector(const vector& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){_start = new T[v.capacity()];//memcpy(_start, v._start, sizeof(T)*v.size());  不能用memcpy 是浅拷贝for (int i = 0; i  

 但是真的没有问题了吗??看看这个

 但道理来说得打印出9个1  结果呢??

 原因是什么呢,我们先看看resize函数

//重载赋值=(传统)vector& operator=(const vector &v){T* temp = new T [v.capacity()];for(int i=0;i

上一篇:CTF(Web)中关于执行读取文件命令的相关知识与绕过技巧

下一篇:宝塔面板数据库管理详解,轻松上手数据库管理操作!