一、前言
本篇的目的很简单,只有一个:模拟实现vector
如何去模拟实现?我们可以看看vector的源码,我们可以抽离出主体框架:
template <class T, class Alloc = alloc>
class vector {
typedef T value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
protected:
iterator start;
iterator finish;
iterator end_of_storage;
}
这本质上与T*a,size_t size,size_t capacity是类似的:
对于size = _finish - _start
对于capacity = _endofstorage-_start
有了这些作为铺垫,我们对于vector的模拟实现大概有了一个基本的框架,话不多说,直接进入主题👇
二、无参构造&析构
对于这两个都是我们熟悉的老朋友了
- 无参构造
初始化全设置为空:
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
- 析构
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
三、基础接口
1.empty和clear
empty
bool empty() const
{
return _finish == _start;
}
clear
void clear()
{
_finish = _start;//这里可不是置为nullptr哦
}
2.size和capacity
size
size_t size() const
{
return _finish - _start;
}
capacity
size_t capacity() const
{
return _endofstorage - _start;
}
3.[]和iterator
[]
提供const版本和非const版本:
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
iterator
同理普通迭代器和const迭代器版本,同理,范围for循环此时也是可以实现的:
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;
}
四、resize和reserve
这两个接口需要单独拎出来,这是因为后面的插入等相关操作需要用到,所以我们先来看看这两个接口,同时这里有一些问题值得我们去注意:
resize
n个数据去初始化,这个n是多大,会造成什么影响?我们需要进行分类讨论:
//分情况
void resize(size_t n, const T& val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
resize的参数初始化值为T类型的构造,这里可不能直接初始化为0,要是T是自定义类型呢?是vector呢?所以这里如果T是vector的化调用的就是vector的构造函数。另外,这里还需要注意的一点是:构造vector的时候是匿名对象,匿名对象具有常性,不可修改所以要加上const修饰
所以,我们自然而然可以知道,对于内置类型比如int,都是有构造函数的:
reserve
reserve最大的问题就是深拷贝!开辟新空间进行赋值的时候如果直接使用memcpy是浅拷贝
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
//size()需要先保存起来,后面_start会发生改变
size_t sz = size();
//为空不需要拷贝了
if (_start)
{
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝
//delete[] _start;
}
_start = tmp;
_finish = _start+sz;
_endofstorage = _start + n;
}
}
- size()需要先用sz保存
为啥?如果没有保存:
_start = tmp; _finish = _start+size(); _endofstorge = _start+n;
不要忘了:size()=_finish-_start, 而_start = tmp会更新_start;根本就不是原来的size()了
- 使用memcpy问题
memcpy拷贝数据,拿vector<vector>作为例子,其中vector仍然是浅拷贝的,对于自定义类型出现问题
vector<vector<int>> vv; vector<int> v(4, 1); //复用push_back尾插 vv.push_back(v); vv.push_back(v); vv.push_back(v); vv.push_back(v); //需要扩容成2倍 vv.push_back(v); for (size_t i = 0; i < vv.size(); i++) { for (size_t j = 0; j < vv[i].size(); j++) { cout << vv[i][j] << " "; } cout << endl; }
五、尾插尾删
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
}
*_finish = x;
++_finish;
}
void pop_back()
{
assert(_finish > _start);
--_finish;
}
六、其他构造
这里需要复用前面的一些接口,所以放在这个地方
- 迭代器区间构造
这里复用了push_back,而且写成了模板
template <class InputIterator>
//模板可以使用其他迭代器区间
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);//int不能解引用
++first;
}
}
类模板的成员函数可以是函数模板,使之可以是任意类型的迭代器区间,包括了自身的迭代器区间构造
另外,初始化列表全部初始化为nullptr,没有初始化就是随机值,出现野指针
- 拷贝构造
初始化列表全都要初始化为nullptr,否则就是随机值
//写法1
vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
reserve(v.capacity());
for (const auto& e : v)
{
push_back(e);
}
}
//写法2
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
- 赋值重载
赋值重载需要复用拷贝构造
//缺陷:自己拷贝自己
//v1(v2)
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
这种写法就是有一个小问题:如果是自己拷贝自己呢?加个判断?没用,因为此时已经传值传参过来了,加个判断没啥意义了。但是这个问题不大,我们允许存在,平时自己也很少自己赋值自己。
另外,这里是传值调用,有人会说了:传引用也可以啊,此时如果是引用的话,v2赋值给v1,v1不是v2的拷贝,直接把v2换成了v1,v1换给了v2,v2本身已经发生变化了,这不是赋值了。
七、迭代器失效
1.insert
insert这个太熟悉了,废话不多说直接上手代码:
//迭代器失效:扩容引起野指针问题
void insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
}
测试代码:
void Test3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if (it != v.end())
{
v.insert(it, 30);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
这是因为扩容导致pos失效了:
insert过程中发生扩容,导致it指向的空间实际上已经被释放,it指向已被释放的空间是野指针,造成了迭代器失效
所以,我们应该去更新pos,算出pos刚开始的相对位置,然后再去进行更新即可解决问题。但是此时外面调用insert的it仍然是失效的,因为是传值调用,形参改变不影响实参,可以通过返回值接收解决问题。(如果是传引用的话,只能传变量,而临时对象具有常性,不能调用,存在很多问题),所以直接用返回值解决。
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
//扩容会导致pos迭代器失效,需要更新
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 = val;
++_finish;
return pos;
}
测试代码:
void Test3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
v.insert(v.begin(), 0);
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if (it != v.end())
{
v.insert(it, 30);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
2.erase
挪动数据进行覆盖即可
void erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
}
erase的pos也可能会导致pos失效,测试代码:
void Test6()
{
//删除所有偶数
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
v.erase(it);
}
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
测试发现:
1,2,3,4的时候发生崩溃
1,2,2,3,5结果只删了一个2
1,2,3,4,5结果是正常的
上述代码在VS下,当erase(it)之后,it指向的位置发生改变,然后在++it的话,会出现问题,出现一些错误,造成迭代器失效。
我们最好统一认为失效了。
正确的erase:
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
测试代码:
void Test6()
{
//删除所有偶数
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
else
{
++it;
}
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
对于insert和erase存在迭代器失效的问题,当迭代器失效而来,我们就不要再去访问pos的位置了,要更新pos的位置,可通过返回值接收进行访问,
八、memcpy问题
如果拷贝的是内置类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝,指向同一块空间,假设我们仍然在reserve接口中使用memcpy进行拷贝:
我们以vector类为例子:
void Test10()
{
vector<string> v;
v.push_back("11111111111111111111");
v.push_back("22222222222222222222");
v.push_back("33333333333333333333");
v.push_back("44444444444444444444");
v.push_back("55555555555555555555");
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
所以我们要调用自己的拷贝,一个一个进行深拷贝。
九、vector.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
namespace hwc
{
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;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
/*vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
reserve(v.capacity());
for (const auto& e : v)
{
push_back(e);
}
}*/
//vector<int> v1(10, 5);
//vector<char> v2(10, 'A');
vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
//改成int或强转
vector(int n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(n);
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
template <class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);//int不能解引用
++first;
}
}
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
//缺陷:自己拷贝自己
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
size_t sz = size();
//为空不需要拷贝了
if (_start)
{
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝
//delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
//分情况
void resize(size_t n, const T& val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
bool empty() const
{
return _finish == _start;
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
}
*_finish = x;
++_finish;
}
void pop_back()
{
assert(_finish > _start);
--_finish;
}
//迭代器失效:野指针问题
/*void insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos < _finish);
if (_finish == _endofstorge)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
}*/
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
//扩容会导致pos迭代器失效,需要更新
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 = val;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
void clear()
{
_finish = _start;
}
public:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
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);
cout << v.size() << endl;
cout << v.capacity() << endl;
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void Test2()
{
vector<int> v;
v.resize(10, -1);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
v.resize(5);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
v.pop_back();
v.pop_back();
v.pop_back();
v.pop_back();
v.pop_back();
v.pop_back();
}
void Test3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
v.insert(v.end(), 0);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if (it != v.end())
{
v.insert(it, 30);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void Test4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if (it != v.end())
{
//传值
v.insert(it, 30);
}
//insert以后it不能使用,可能迭代器失效(野指针)
//(*it)++;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void Test5()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if (it != v.end())
{
v.erase(it);
}
cout << *it << endl;
// (*it)++;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void Test6()
{
//删除所有偶数
vector<int> v;
v.push_back(1);
v.push_back(2);
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())
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
else
{
++it;
}
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void Test7()
{
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> v1(v);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vector<int> v2;
v2.push_back(10);
v2.push_back(20);
v2.push_back(30);
v1 = v2;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
v1 = v1;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void Test8()
{
string str("hello world");
vector<int> v(str.begin(), str.end());
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
/*vector<int> v1(v.begin(), v.end());*/
vector<int> v1(10, 5);
//vector<char> v2(10, 'A');
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows);
for (size_t i = 0; i < vv.size(); i++)
{
vv[i].resize(i + 1, 0);
vv[i][0] = vv[i][vv[i].size() - 1] = 1;
}
for (size_t i = 0; i < vv.size(); i++)
{
for (size_t j = 0; j < vv[i].size(); j++)
{
if (vv[i][j] == 0)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
}
return vv;
}
};
void Test9()
{
vector<vector<int>> vvRet = Solution().generate(5);
for (size_t i = 0; i < vvRet.size(); i++)
{
for (size_t j = 0; j < vvRet[i].size(); j++)
{
cout << vvRet[i][j] << " ";
}
cout << endl;
}
/*vector<vector<int>> vv;
vector<int> v(10, 1);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
for (size_t i = 0; i < vv.size(); i++)
{
for (size_t j = 0; j < vv[i].size(); j++)
{
cout << vv[i][j] << " ";
}
cout << endl;
}*/
cout << endl;
}
void Test10()
{
vector<string> v;
v.push_back("11111111111111111111");
v.push_back("22222222222222222222");
v.push_back("33333333333333333333");
v.push_back("44444444444444444444");
v.push_back("55555555555555555555");
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
}