Vector深度剖析及模拟实现
目录
一、vector的结构
二、size,capacity,reserve和operator[]函数的实现
三、push_back函数的实现
四、迭代器的实现
五、const迭代器的实现
六、print_vector的模板化
七、pop_back函数的实现
八、insert函数的实现
九、erase函数的实现
十、resize函数的实现
十一、默认构造函数,析构函数和拷贝构造函数
十二、赋值运算符重载
12.1 第一种写法
12.2 第二种写法
十三、代码
13.1 vector.h
13.2 test.cpp
一、vector的结构
在std中的vector是用模板来实现的,我们也使用模板。vector主要有三个成员变量,分别是T指针类型的_start,_finish,_end_of_storage.使用他们三个就可以构建一个vector。因为vector是连续存放的,所以我们也可以使用原生指针来模拟迭代器的实现。std中的vector是用模板来实现的。
代码如下:
namespace zx
{template<class T>class vector {public:typedef T* iterator;private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
}
其中_start指向的是数据的起始位置,
_finish指向的是最后一个数据的后一个位置,
_end_of_storage指向的是最后一个空间的下一个位置
二、size,capacity,reserve和operator[]函数的实现
size_t size()
{return _finish - _start;
}
size_t capacity() {return _end_of_storage - _start;
}
T& operator[](size_t i) {assert(i < size());return _start[i];
}
void reserve(size_t n) {if (n > capacity()) {T* tmp = new T[n];size_t old_size = size();memcpy(tmp, _start, size() * sizeof(T));delete[] _start;_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}
}
注意reserve函数,我们要新创建一个old_size变量来存放原来的size,这是因为当我们更新_finish的时候,需要使用_start加上size,如果不保存的话,size内部的逻辑是_finish-_start.此时我们的_start已经更新了,得到的size就会出错。
三、push_back函数的实现
当我们往vector里面尾插数据的时候,我们要先来判断容量满了没有,如果满了就需要扩容。代码如下:
void push_back(const T& x) {//满了,需要扩容if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;
}
测试代码如下:
void test1() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); i++) {cout << v[i] << " ";}cout << endl;
}
四、迭代器的实现
iterator begin() {return _start;
}
iterator end() {return _finish;
}
测试代码如下:
void test2() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;for (auto ele : v) {cout << ele << " ";}cout << endl;
}
五、const迭代器的实现
当我们想要实现一个打印vector数据的函数时,会出现bug,代码如下:
void print_vector(const vector<int>& v) {vector<int>::iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;
}
void test3() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);print_vector(v);
}
这是因为我们的print_vector函数里面的vector是const,const对象只能调用const函数,不能调用非const函数,所以我们就需要写一个const迭代器。代码如下:
typedef const T* const_iterator;
const_iterator begin() const
{return _start;
}
const_iterator end() const
{return _finish;
}以下代码放在zx类域里面,不是放在vector类模板里面
void print_vector(const vector<int>& v) {vector<int>::const_iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;
}
测试代码如下:
void test3() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);print_vector(v);
}
由于还有const对象,所以我们需要修改以下函数和新增const的operator[]函数
const_iterator end() const
{return _finish;
}
size_t size() const
{return _finish - _start;
}
T& operator[](size_t i) {assert(i < size());return _start[i];
}
const T& operator[](size_t i) const
{assert(i < size());return _start[i];
}
六、print_vector的模板化
在上面的print_vector函数里面,我们只能打印vector<int>类型的数据如果我们是vector<double>呢?就需要重新写一个print_vector函数来打印double类型。于是我们就可以将这个函数写成模板的形式。代码如下:
template<class T>
void print_vector(const vector<T>& v) {vector<T>::const_iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;
}
但是我们修改之后运行,会有一个bug如下所示:

这是由于c++语法的规定,不能去没有实例化的类模板里面去取东西,编译器不能区分这里的const_iterator是类型还是静态成员变量,所以我们就需要在前面加上一个typename,或者使用auto。代码如下所示:
template<class T>void print_vector(const vector<T>& v) {typename vector<T>::const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;}
七、pop_back函数的实现
这个函数是尾删最后一个元素,在删除之前,需要判断vector是否为空。代码如下:
bool empty() {return _start == _finish;
}
void pop_back() {assert(!empty());--_finish;
}
八、insert函数的实现
再插入数据之前先扩容,代码如下:
iterator insert(iterator pos, const T& x) {if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : 2 * capacity());}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;
}
测试代码如下:
void test4() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);print_vector(v);v.insert(v.begin() + 2, 30);print_vector(v);}
运行如下:

我们发现运行时没有问题的,
上面是没有扩容时候的情况,如果我们需要扩容呢?
代码如下:
void test4() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_vector(v);v.insert(v.begin() + 2, 30);print_vector(v);}
此时运行,发现出bug了,这是为什么呢?这就是经典的迭代器失效的问题。
这是因为,我们扩容之后,_start,_finish,_end_of_storage都改变了,而pos指向的还是之前的旧空间的位置,所以就出bug了。修改代码如下:
iterator insert(iterator pos, const T& x) {if (_finish == _end_of_storage) {//记录相对位置size_t relative = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + relative;}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;
}
如果我们想在第二个位置插入40,然后将第二个位置的值*10.会发生上面呢?代码如下:
void test5() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_vector(v);auto pos = v.begin() + 1;v.insert(pos, 40);(*pos) *= 10;print_vector(v);
}
运行结果如下:

我们发现第二个位置没有乘以10,这是因为pos还是指向的旧空间。
所以,我们在insert之后,pos就失效了,不要直接访问,要访问就要更新这个失效的迭代器的值。
在vs编译器会进行强制的pos检查。如果插入,无论扩容没有,都不能使用这个pos,访问就会报错。
九、erase函数的实现
void erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()) {*(it - 1) = *it;it++;}_finish--;
}
我们使用erase来测试一段代码,来删除偶数,如下所示:
void test6() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(5);auto it = v.begin();while (it!=v.end()) {if (*it % 2 == 0) {v.erase(it);}it++;}print_vector(v);
}
运行之后结果不是我们想要的,有时候还会报错。
我们在删除一个vector数据之后,会导致原本的迭代器的意义发生变化,在vs编译器里面,如果我们使用迭代器删除一个数据之后,再次使用这个迭代器,会直接报错。所以在vs编译器里面,erase也会返回一个迭代器,它指向刚刚被删除元素的下一个位置。代码如下:
iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()) {*(it - 1) = *it;it++;}_finish--;return pos;
}
测试删除偶数的代码如下:
void test6() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(5);auto it = v.begin();while (it!=v.end()) {if (*it % 2 == 0) {it=v.erase(it);}else {it++;}}print_vector(v);
}
我们在insert和erase之后,可能都会导致迭代器失效,所以insert会返回插入元素的迭代器,erase会返回删除元素下一个的迭代器。用来更新迭代器。
十、resize函数的实现
resize的函数原型如下:

当我们的n大于原本的size时,可能需要插入数据,如果传入了数据,就使用传入的数据,如果没有传入数据,就会创建一个匿名对象,这个匿名对象会调用默认构造函数,然后将值赋给val。为什么不使用0呢?这是因为vector里面的数据类型可能是自定义类型。如果是int或者double类型呢?int和double是没有构造函数的概念的。但是为了兼容自定义类型,还是可以使用int()。代码如下:
void resize(size_t n,T val=T())
{if (n<size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;_finish++;}}
}
十一、默认构造函数,析构函数和拷贝构造函数
在上面的代码中,我们是没有写构造函数,析构函数和拷贝构造的,我们使用的都是类里面默认生成的,默认构造函数和析构函数都默认是空,拷贝构造函数时浅拷贝。当我们使用拷贝构造时,会导致浅拷贝,也就是一个字节一个自己的拷贝,但是我们的vector类里面是从堆区new出来的空间,使用拷贝构造时,会导致这两个vector指向同样的空间,代码如下所示:

我们打开监视窗口,发现两个vector<int> 里面的参数是一模一样的。这就是经典的浅拷贝问题。这个程序解释之后是不会报错的,这是因为我们默认的析构是空的,不会析构两次,但是如果我们在堆区申请了空间,是需要写析构函数的,我们先写一个析构函数,代码如下:
~vector()
{if (_start) {delete _start;_start = _finish = _end_of_storage = nullptr;}
}
然后再次执行上述的代码,发现程序崩了,这是因为同一块空间析构了两次。所以我们就需要重新写一个拷贝构造函数来实现深拷贝,但是在写拷贝构造函数之前,我们还需要写一个默认构造函数,这是因为如果我们没有写构造函数,会默认生成构造函数和拷贝构造,如果我们写了构造函数,就不会自动生成构造函数了,所以我们在写拷贝构造之前,需要写一个构造函数。代码如下:
vector(){}
vector(const vector<T>& v)
{reserve(v.size());for (auto& e : v) {push_back(e);}
}
十二、赋值运算符重载
12.1 第一种写法
void clear() {_finish = _start;
}
vector<T>& operator=(const vector<T>& v) {if (_start != v._start) {clear();reserve(v.size());for (auto& e : v) {push_back(e);}}return *this;
}
12.2 第二种写法
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<T>& operator=(vector<T> v) {swap(v);return *this;
}
十三、代码
13.1 vector.h
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
#include<string.h>namespace zx
{template<class T>class vector {public:typedef T* iterator;typedef const T* const_iterator;vector(){}vector(const vector<T>& v){reserve(v.size());for (auto& e : v) {push_back(e);}}~vector(){if (_start) {delete _start;_start = _finish = _end_of_storage = nullptr;}}void clear() {_finish = _start;}/*vector<T>& operator=(const vector<T>& v) {if (_start != v._start) {clear();reserve(v.size());for (auto& e : v) {push_back(e);}}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<T>& operator=(vector<T> v) {swap(v);return *this;}iterator begin() {return _start;}iterator end() {return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}T& operator[](size_t i) {assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}void reserve(size_t n) {if (n > capacity()) {T* tmp = new T[n];size_t old_size = size();memcpy(tmp, _start, size() * sizeof(T));delete[] _start;_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}}void push_back(const T& x) {//满了,需要扩容if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}bool empty() {return _start == _finish;}void pop_back() {assert(!empty());--_finish;}iterator insert(iterator pos, const T& x) {if (_finish == _end_of_storage) {//记录相对位置size_t relative = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + relative;}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()) {*(it - 1) = *it;it++;}_finish--;return pos;}void resize(size_t n,T val=T()) {if (n<size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;_finish++;}}}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};template<class T>void print_vector(const vector<T>& v) {typename vector<T>::const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;}void test1() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); i++) {cout << v[i] << " ";}cout << endl;}void test2() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;for (auto ele : v) {cout << ele << " ";}cout << endl;}void test3() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);print_vector(v);}void test4() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_vector(v);v.insert(v.begin() + 2, 30);print_vector(v);}void test5() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_vector(v);auto pos = v.begin() + 1;v.insert(pos, 40);(*pos) *= 10;print_vector(v);}void test6() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(5);auto it = v.begin();while (it!=v.end()) {if (*it % 2 == 0) {it=v.erase(it);}else {it++;}}print_vector(v);}void test7() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<int> v1 = v;}void test8() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<int> v1;v1.push_back(1);v1.push_back(2);v1 = v;print_vector(v1);}
}
13.2 test.cpp
#include"vector.h"int main() {//zx::test1();//zx::test2();//zx::test3();//zx::test4();//zx::test5();//zx::test6();//zx::test7();zx::test8();return 0;
}
