手写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_back
和insert
能不能调用同一个函数呢?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; }
我的代码基本和我分析的一样,没有什么改动,大家可以看一下。