前言:
了解学习完vector的底层之后,写一篇vector模拟实现,梳理一下知识点,帮助自己加深对vector的理解,以及以后得复习使用。()常用部分)
关于vector 是用迭代器(本部分底层是T*类型)start、finish、endofstorge计算元素个数和容量。
vector 是一种顺序表可以存储多种类型的元素,可以是自定义也可以是内置类型,所以我们加入类模板,同时使用通用类型,定义迭代器:
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
构造函数
vector构造函数,可以使用默认构造,也可以使用迭代器区间初始化,还可以使用类型等构造的方式,代码如下:
vector(size_t n, const T& val = T()) // 并不知道T的类型 可能内置很小 可能很大 所以用引用接收
{
//扩容 并初始化为val
resize(n, val);
}
vector(int n, const T& val = T()) // 并不知道T的类型 可能内置很小 可能很大 所以用引用接收
{
//扩容 并初始化为val
resize(n, val);
}
//使用迭代器区间初始化,为了匹配各种类型的迭代器,所以使用类模板定义
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
//系统默认
vector()
{}
这里需要注意的是:1、c++11 也支持内置烈性的匿名 譬如 int() char() 都可以,
2、我们使用resize 默认开辟空间 并将空间初始化成 val 我在将resize的地方通过代码详细说明。
3.使用迭代器区间初始化,也是vector底层允许的,为了匹配各种类型的迭代器,我们使用类模板定义一个兼顾各种类型。
实现代码如下:
void test_vector2()
{
manvec::vector<int> v1(10, 1);
manvec::vector<string>v2(10,"hello word");
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
manvec::vector<int> v3(v1.begin(), v1.end());
string str("nihao shijie");
manvec::vector<string> v4(v2.begin(), v2.end());
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
int a[] = { 16,2,77,29 };
manvec::vector<int> v5(a, a + 4);
for (auto e : v5)
{
cout << e << " ";
}
cout << endl;
}
拷贝构造
//拷贝构造
vector(const vector<T>& v)
{
_start = new T[v.capacity()];//不确定T类型 有可能是自定义
//在这里 使用了 赋值 有效避免了 memcpy的浅拷贝
memcpy(_start, v._start, sizeof(T) * v.size());
/*for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}*/
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
//交换
这里需要注意的是,这种拷贝,存在隐患,如果只是简单地内置类型,是完全没问题的。
如下图所示
但是如果是自定义类型的呢?
如果想避免这个问题,就要把重载部分模拟实现。
重载
交换
在介绍赋值重载之前 我们先引入交换函数 我们这里使用的是std里自带的交换函数,代码如下:
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
我们可以看看std:swap底层实现;帮助我们理解
=赋值重载
//v1 = v2
vector<T>& operator=(vector<T> v) //再浏览一下
{
swap(v);
return *this;
}
这里形参部分是v2的拷贝构造,在完成交换之后,函数结束,形参的声明周期也就结束了。但是我们已经通过引用接受返回的*this 就是v1。
[]重载
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
reserve函数
void reserve(size_t n)
{
//先判断是否需要扩容
if (n > capacity())
{
// 记录原始 vector 的size
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//同样避免浅拷贝 地址失效
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[]_start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
这个地方如果我们使用的是memcpy 将_start指向的空间释放会发生什么呢? tmp是不是也就变成野指针了?如下图所示。
所以,对于T是string这样的深拷贝的类,调用的是string赋值重载,实现string对象的深拷贝。
resize函数
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
插入函数
因为插入的时候,会面临迭代器失效的问题,所以我要把这个函数理一遍,要保证插入的位置在合理的范围内,
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start && pos <= _finish);
// 如果扩容的的话 会存在迭代器失效,所以我们要记录pos相对于_start的位置
size_t len = pos - _start;
if (_finish == _endofstorage)
{
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;
return pos;
}
图解如下:
擦除函数
对删除位置的pos 位置之后 迭代器可能失效,vs进行了强制检查,如果访问就会直接报错,
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finish);
iterator cur = pos + 1;
while (cur!= _finish)
{
*(cur - 1) = *cur;
++cur;
}
--_finish;
return pos;
}
整体模拟实现
#pragma once
#include<assert.h>
namespace manvec
{
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
vector(size_t n, const T& val = T()) // 并不知道T的类型 可能内置很小 可能很大 所以用引用接收
{
//扩容 并初始化为val
resize(n, val);
}
vector(int n, const T& val = T()) // 并不知道T的类型 可能内置很小 可能很大 所以用引用接收
{
//扩容 并初始化为val
resize(n, val);
}
//使用迭代器区间初始化,为了匹配各种类型的迭代器,所以使用类模板定义
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
//系统默认
vector()
{}
//拷贝构造
vector(const vector<T>& v)
{
_start = new T[v.capacity()];//不确定T类型 有可能是自定义
//在这里 使用了 赋值 有效避免了 memcpy的浅拷贝
memcpy(_start, v._start, sizeof(T) * v.size());
/*for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}*/
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
//交换
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
//v1 = v2
T& operator=(vector<T> v) //再浏览一下
{
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[]_start;
_start = _finish = _endofstorage = nullptr;
}
}
void reserve(size_t n)
{
//先判断是否需要扩容
if (n > capacity())
{
// 记录原始 vector 的size
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//同样避免浅拷贝 地址失效
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[]_start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
void push_back(const T& x)
{
/*if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
++finish;*/
insert(end(), x);
}
T& operator[](size_t pos)
{
assert(pos< size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
//获取容量
size_t capacity() const
{
return _endofstorage - _start;
}
//获取填充个数
size_t size() const
{
return _finish - _start;
}
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start && pos <= _finish);
// 如果扩容的的话 会存在迭代器失效,所以我们要记录pos相对于_start的位置
size_t len = pos - _start;
if (_finish == _endofstorage)
{
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;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finish);
iterator cur = pos + 1;
while (cur!= _finish)
{
*(cur - 1) = *cur;
++cur;
}
--_finish;
return pos;
}
void print(const vector<int>& v)
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
private:
//c++11 支持在定义的时候给缺省值
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};
}