vector模拟实现
1. vector框架
namespace bit
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
}
_start 首元素地址
_finish 尾元素后的地址
_end_of_storage 指向最后空间的地址
capacity是计算这个空间的大小
size是计算空间中存储的个数
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
2. 实现vector遍历功能
插入数据
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
++_finish;
}
预处理开辟空间
void reserve(size_t n)
{
if (n > capacity())
{
// oldsize这里是要给_finish准备的
size_t oldsize = size();
T* tmp = new T[n];
if (_start) {
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
}
_start = tmp;
// _start是新空间的首元素地址
// 这里使用_finish = _start + size();对不对,不对因为size返回的是之前空间的_finish - 新空间的_start,从而导致size不是元素个数
_finish = _start + oldsize;
_end_of_storage = _start + n;
}
}
在这里注意这里的memcpy只是浅拷贝,只是把字节赋值过去
当发生扩容时,我要释放旧空间时,旧空间中的string对象先发生析构,最后再释放这块空间。导致新空间的_start就是野指针,如何解决?进行赋值拷贝
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
T* tmp = new T[n];
if (_start)
{
for (int i = 0; i < oldsize; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + oldsize;
_end_of_storage = _start + n;
}
}
operator[]
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
测试
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
迭代器遍历
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
for (auto e : v1)
{
cout << e << " ";
}
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " ";
++it;
}
在这里补充一个小点
.h不会编译,在预处理阶段.h会在.cpp展开
展开后会向上查找,什么意思呢就比如
#include<iostream>
using namespace std;
#include"vector.h"
我头文件vector.h在#include<iostream>,using namespace std;
下面,如果我.h在文件中包含这头文件的库函数,就能使用,因为展开后向上查找
但是如果vector.h在#include<iostream>,using namespace std;
上面,.h里面的代码就不能使用#include<iostream>,using namespace std;
3. vector增删查
增
void insert(iterator pos, const T& x)
{
if (_finish == _end_of_storage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
这里会出现迭代器失效的问题
为什么这里一扩容就会出现随机值
这里我们不难发现pos不在_start与_finish区间
也就是说
原来的空间已经被释放而pos还在原来空间的位置,我们应该如何解决迭代器失效,pos变成野指针的问题
当扩容时,更新pos的位置
void insert(iterator pos, const T& x)
{
if (_finish == _end_of_storage)
{
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 = x;
++_finish;
}
在main函数中
v1.insert(it, 1000);
思考:这里的实参能不能影响形参
影响不了,这里是传值返回,所以说当发生扩容时形参pos发生改变而实参it还在原来空间的地址,变成野指针,在这里我们是这样处理的
把void换成iterator
iterator insert(iterator pos, const T& x)
{
//之前内容不变
return pos;
}
在main函数中重新更新一下it
it = v1.insert(it, 1000);
查
这里使用库里面<algorithm>的函数
改
erase
在main函数中
std::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
int x;
cin >> x;
std::vector<int>::iterator it = find(v1.begin(), v1.end(), x);
if (it != v1.end())
{
// 界定it失效
v1.erase(it);
cout << *it << endl;
}
cout << typeid(it).name() << endl;
删除空间it后,只要访问it(删除值的位置)就报错失效,在VS下直接报错
比如:
cout << *it << endl
为什么呢?第一可能缩容,第二有可能失效变成野指针,VS下检查严格直接报错
那我想访问it应该怎么操作----->先更新it的位置,再it前面判断防止出现野指针
v1.erase(it);
if (it != v1.end())
cout << *it << endl;
4. vector构造与析构
拷贝构造
vector(const vector<T>& v)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
当写了拷贝构造后,编译器的默认构造就不管用了
vector<int> v1;//发生报错
有两种方法解决
法一
vector()
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{}
法二
// 强制编译器生成默认的
vector() = default;
利用函数模板进行拷贝
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector<int> v2(v1.begin() + 1, v1.end());
拷贝
vector(size_t n, const T& val = T())
{
reserve(n);
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
const T& val = T()
这里使用了匿名对象当缺省值
当然这样写例如 vector<int> v1(10,0);上面的代码会报错,理由就是 vector(InputIterator first, InputIterator last)这种声明更合适,为了更好运行此代码,我们再添加一下,下面的代码,实参形参类型更匹配
vector(int n, const T& val = T())
{
reserve(n);
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
析构
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
}
5. vector赋值
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
//v1 = v3
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
上面使用的是现代写法
用的引用返回因为v1在main的栈内,不发生析构