7.手写Vector(模仿STL-vector)(只有封装思想)

手写Vector(模仿STL-vector)(只有封装思想)

 /*************************************************************************
         > File Name: vector.cpp
         > Author:Xiao Yuheng
         > Mail:3312638794@qq.com
         > Created Time: Wed Nov  1 07:04:57 2023
  ************************************************************************/
 ​
 #include <iostream>
 #include <cstring>
 ​
 using namespace std;
 ​
 int *allocate(int n) { return new int[n]; }
 void destroy(int *data) { delete[] data; }
 ​
 class Vector{
 public:
     Vector(int n = 0) : n(n), first(n > 0 ? allocate(n) : nullptr), last(first + n), now(first + n) {};
     Vector(const Vector &);
     int *insert(int *, const int &);
     void __insert(int *, const int &);
     void push_back(const int &);
     int size() { return now - first; }
     int &operator[](int ind) { return *(first + ind); }
     int front() { return *first; }
     int back() { return *(now - 1); }
     int *begin() { return first; }
     int *end() { return now; }
     void pop_back();
     void clear() { now = first; }
     void erase(const int, const int);
     bool empty() { return now == first ? true : false; }
     ~Vector();
 private:
     int n;
     int *first, *last, *now;
 };
 ​
 void construct(int *now, const int &x) { new(now) int(x); }
 ​
 // 拷贝构造
 Vector::Vector(const Vector &vec) : n(vec.n),
                                     first(n > 0 ? allocate(n) : nullptr),
                                     last(first + n),
                                     now(first + (vec.now - vec.first)) {
     for (int i = 0; i < n; i++) {
         construct(first + i, vec.first[i]);
     }
 }
 ​
 int *copyInt(int *nowBegin, int *oldBegin, int *oldEnd) {
     memmove(nowBegin, oldBegin, sizeof(int) * (oldEnd - oldBegin));
     return nowBegin + (oldEnd - oldBegin);
 }
 ​
 void Vector::__insert(int *ind, const int &x) {
     if (now != last) {
         construct(now, *(now - 1));
         ++now;
         copyInt(ind + 1, ind, now - 1);
         *ind = x;
     }
     else {
         const int oldSize = size();
         const int len = oldSize != 0 ? 2 * oldSize : 1;
         int *newFirst = allocate(len);
         int *newNow = newFirst;
         newNow = copyInt(newFirst, first, ind);
         construct(newNow, x);
         ++newNow;
         newNow = copyInt(newNow, ind, last);
         destroy(first);
         first = newFirst;
         now = newNow;
         last = newFirst + len;
     }
 }
 ​
 void Vector::__insert(int *ind, const int &x) {
     if (now != last) {
         construct(now, *(now - 1));
         ++now;
         copyInt(ind + 1, ind, now - 1);
         *ind = x;
     }
     else {
         // 分配新地址
         const int oldSize = size();
         const int len = oldSize != 0 ? 2 * oldSize : 1;
         int *newFirst = allocate(len);
         int *newNow = newFirst;
         // 把地址移动到新地址上面去
         newNow = copyInt(newFirst, first, ind);
         construct(newNow, x);
         ++newNow;
         newNow = copyInt(newNow, ind, last);
         // 删除源地址
         destroy(first);
         // 更新地址
         first = newFirst;
         now = newNow;
         last = newFirst + len;
     }
 }
 ​
 void Vector::push_back(const int &x) {
     if (now != last) {
         construct(now, x);
         ++now;
     }
     else
         __insert(now, x);
 }
 ​
 void Vector::pop_back() {
     if (empty()) return ;
     --now;
 }
 ​
 void Vector::erase(const int first, const int last) {
     copyInt(this->first + first, this->first + last + 1, now);
     now -= (last - first + 1);
 }
 ​
 Vector::~Vector() {
     delete[] first;
 }
 ​
 int main() {
     Vector vec;
     cout << "push_back : " << endl;
     for (int i = 0; i < 10; i++) {
         vec.push_back(i);
     }
     for (int i = 0; i < vec.size(); i++) {
         cout << vec[i] << endl;
     }
     cout << "pop_back : " << endl;
     while (vec.begin() != vec.end()) {
         vec.pop_back();
     }
     cout << vec.size() << endl;
     cout << "insert : " << endl;
     for (int i = 0; i < 10; i++) {
         vec.insert(&vec[0], i);
     }
     for (int i = 0; i < vec.size(); i++) {
         cout << vec[i] << endl;
     }
     cout << "erase(first, last) : " << endl;
     vec.erase(1, 5);
     for (int i = 0; i < vec.size(); i++) {
         cout << vec[i] << endl;
     }
     cout << "clear() : " << endl;
     vec.clear();
     cout << vec.size() << endl;
     return 0;
 }

解析

在这段代码中自我感觉关键的一点是push_back()函数和insert()函数(因为就看懂了这一点东西),下面我来对照这源码来讲解一下自己的代码。

push_back分析

我们先来看看stl中的源码是什么样的:

源码中的这一段是什么意思嘞?下面说说我的看法:

 // _M_finish 和 _M_end_of_storage分别代表什么含义呢?我来说说我的看法(简单分析一手)
 // _M_finish 是现在数组中已经存的数的尾地址
 // _M_end_of_storage 是数组开辟空间的尾地址
 // 在扩展一个 _M_start  数组首地址
 // 好,这样我们在接着往下看

在339行它进行了一个判断,判断空间是否满了,如果没有的话,我们就把__x的值加入到数组中,然后把_M_finish的地址往后移动一位。

我们先来看看源码中construct是怎么实现的,源码如下:

我们可以发现这个地方就是用了一个原地构造来实现的,好大家在来看看我的代码

 void construct(int *now, const int &x) { new(now) int(x); }
 ​
 void Vector::push_back(const int &x) {
     if (now != last) {
         construct(now, x);
         ++now;
     }
     else
         __insert(now, x);
 }

可以发现结构是一模一样的(没有自己的思想,先学着写,啊哈哈哈哈哈哈哈哈哈),但是呢?它传的是一个模板类型,然后在进行来原地构造,(这里由于还没学,就先不写了),它可以适用很多种情况,如果传进来的是个指针类型,原地构造也是可以很好解决的,我们这里只有一个int类型,但是设计思路应该是这样的。

如果空间满了,我们就调用_M_insert_aux()这个函数,我们进去看一下,这个函数是什么呢?

霍,这么大一片,我们来慢慢分析,我们先来看看这个名字_M_insert_aux()这个名字看着怎么是insert的一个分支呢?怎么不是很像push_back函数应该调用的东西呢?我们来思考一下,push_backinsert能不能调用同一个函数呢?push_back是不是就是一个特殊的insert呢?(插入地点为尾地址)没错,这里就是我学到的第一个点,函数的复用性,可以都调用同一个函数。这段代码我们在下面讲insert的时候来讲。

insert分析

我们先来还是先从源码开始看起

他先求了一下他插入位置距离开始位置有多远,为什么要求这个值呢?有什么用呢?他最后返回了一个地址,可begin() + __n不是和__position是一样的吗?不不不,这可不一样,因为我们的begin()可是会变的,因为我们可能会进行扩容操作,这时begin可就变化了,所以我们要先求一下__n的值,以便最后返回,好,我们接着往下看。

在362行代码中,他先进行了一个判断,判断这个数组是否满了,并且插入的是末尾,如果是的话,我们就会进行和push_back没满一样的操作,我们在这里省略,如果满了,就调用_M_insert_aut函数。

我们来看看这段最核心的代码(自我认为),它先进行了一个判断,判断这个函数满没有满,如果没有的话,我们就调用construct函数,先把_M_finish这一位的值给占用,然后运行++_M_finish,然后在进行一个copy_backward函数,我们进入这个函数看看里面有什么东西:

经过不断调用,最后调用了这样一个函数,这个函数是干嘛的嘞? ​ 解释:memmove 是一个C标准库函数,用于在内存中移动一块数据。它可以处理源地址和目标地址重叠的情况 ​ 没错,这就是一个移动数据的一个函数,所以我们上面的代码中它就是把数据往后移动一格,然后在把值赋给__position

如果满了该怎么办呢?源码中是这样处理的:

  • 先记录下来源数组的大小(644)

  • 在求一下需要把内除扩大到多少(645)

  • 然后分配一下内存(646)

  • 把前半段的数据移动到新数组中(649)

  • 然后插入我们需要插入的值(650 651)

  • 最后在把后半段移动到新数组中(652)

  • 清空原数组(656)

  • 把地址更新一下(658 659 660)

    欧克,这就是我对源代码的理解,现在请大家来看看我们代码:

    void Vector::__insert(int *ind, const int &x) {
         if (now != last) {
             construct(now, *(now - 1));
             ++now;
             copyInt(ind + 1, ind, now - 1);
             *ind = x;
         }
         else {
             // 分配新地址
             const int oldSize = size();
             const int len = oldSize != 0 ? 2 * oldSize : 1;
             int *newFirst = allocate(len);
             int *newNow = newFirst;
             // 把地址移动到新地址上面去
             newNow = copyInt(newFirst, first, ind);
             construct(newNow, x);
             ++newNow;
             newNow = copyInt(newNow, ind, last);
             // 删除源地址
             destroy(first);
             // 更新地址
             first = newFirst;
             now = newNow;
             last = newFirst + len;
         }
     }
     ​
     int *Vector::insert(int *ind, const int &x) {
         int __n = ind - begin();
         if (now != last && ind == end()) {
             construct(now, x);
             ++now;
         }
         else {
             __insert(ind, x);
         }
         return begin() + __n;
     }

    我的代码基本和我分析的一样,没有什么改动,大家可以看一下。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值