手撕vector目录:
一、基本实现思路方针
二、vector的构造函数剖析(构造歧义+拷贝构造)
2.1构造函数使用的歧义问题
2.2 vector的拷贝构造和赋值重载(赋值重载不是构造哦,为了方便写在一起)
三、vector的基本接口
3.1empty和clear
3.2 size和capacity
3.3 [ ]和iterator
四、 resize和reserve
五、尾插尾删
六、迭代器失效
6.1 insert
6.2 erase
七、vector.h
一、基本实现思路方针
本篇的目的很简单,只有一个:模拟实现vector
如何去模拟实现?我们可以看看vector的源码,我们可以抽离出主体框架:
namespace lzy//防止命名冲突
{
template<class T>
class lzy_vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
public:
//成员函数
private:
T* _start;
T* _finish;
T* _end_of_storage;
}
}
对于size = _finish - _start
对于capacity = _endofstorage-_start
可以看到,vector 的底层和 string 一样,都是一个指针指向一块动态开辟的数组,但是二者不同的是,string 是用 T* str 和 _size 和 _capacity 三个成员变量来维护这块空间,而 vector 是用 _finish 和 _end_of_storage 两个指针来维护这块空间;虽然 vector 使用指针看起来难了一些,但本质上其实是一样的 : _size = _finish - _start, _capacity = _end_of_storage - _start;
有了这些作为铺垫,我们对于vector的模拟实现大概有了一个基本的框架,话不多说,直接进入主题👇
二、vector的构造函数剖析(构造歧义+拷贝构造)
1. 无参的构造函数,我们利用初始化列表来进行初始化。用nullptr初始化比较好,因为nullptr的free或delete都不会出错
2. 另一种构造是用n个value值来进行构造,value既有可能是内置类型,也有可能是自定义类型,所以如果用引用作参数的话,需要用const引用,也就是常引用来作参数,否则无法接收内置类型的实参
3. 除无参构造外,常用的构造还有迭代器区间作为参数的构造函数。这里的迭代器需要用函数模板来实现,因为构造vector所用的迭代器不一定只是vector类型的,还有可能是string类型,所以这里的迭代器形参需用模板来实现
lzy_vector() // 初始化列表给空指针
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
//n个val构造
lzy_vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; i++)
push_back(val);
}
//迭代器区间构造
template<class InputIterator>
lzy_vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while (first != last)
{
push_back(*first);
++first;
}
}
吐槽:经过一番折腾,总算是有了个输出结果,要不是reserve没写,要不就是push_back没写,要不就是类模板没搞,要不就是迭代器输出的时候迭代器的begin没写。。。。
2.1构造函数使用的歧义问题
1. 在实现完n个value构造的构造函数之后,如果我们此时用10个int类型的数字1来构造对象v1,实际会报错,报错的原因其实是由于函数的匹配优先级所导致的实参无法正确匹配相应的构造函数。而使用10个char类型的字符A却不会报错,这其实也是由于函数的匹配优先级决定的
2. 对于size_t和常引用作为参数的构造来说,它的匹配优先级对于10个1实际不是最高的,因为常引用需要进行类模板参数T类型的推导,而10又是整型int,int到size_t还需要进行隐式类型转换,代价有点大
而对于迭代器区间作为参数的构造来讲,函数模板参数InputIterator只需要进行一次类型推导即可完成匹配,所以用10个1来构造时,实际匹配的构造函数是迭代器区间作为参数的构造函数,而在匹配的构造函数中,对迭代器区间进行了解引用,那就是对常量10进行了解引用,则发生非法的间接寻址(用了迭代器区间的构造函数,会报错)3 对于这种问题的解决,可以将size_t换成int类型,或者将10强转为size_t类型,但stl源码的解决方式并非是这样的,而是利用了函数重载来解决了这个问题,多重载了一个类型为int的构造函数
好的,我们继续开干!上面的代码运行结果正确
解决方法:
2.2 vector的拷贝构造和赋值重载(赋值重载不是构造哦,为了方便写在一起)
1. 对于拷贝构造的实现,和string一样,还是有传统写法和利用打工人的现代写法,利用打工人的本质实际就是代码重构。传统写就是提前用reserve预留好空间,然后push_back将数据尾插到提前预留的空间当中(这个逻辑还是非常清楚的)
这里让我想起来一个知识点:那会vector介绍接口的时候,我们存在越界访问的问题,我们在reserve之后, 利用[ ] 进行赋值,这样是不对的,因为我们只有capacity,没有size。但是利用push_back插入数据的时候,会进行检查:如果size为0,那么就会赋值,所以reserve之后push_back是合理的
注意:拷贝构造这么写是不对的(我本以为是我的接口写的不完善,但是在std中运行发现也是报错)
原因:
而使用拷贝构造的时候必须这样:
void testvector()
{
lzy::lzy_vector<int> v1(10,1);
lzy::lzy_vector<int> v2(v1);
for(auto e : v2)
{
cout << e << " ";
}
}
int main()
{
testvector();
return 0;
}
2. 无论是从代码可读性还是实用性的角度来讲,现代写法都更胜一筹,这里利用形参对象v的迭代器来构造临时对象tmp,然后将对象tmp和* this进行交换,为了防止对象tmp离开函数栈帧销毁时造成野指针的访问问题,所以在调用构造函数时采用了初始化列表的方式将* this的三个成员都初始化为nullptr(原先埋下的伏笔,居然在这里展现出来了)
注:别忘了重新写swap函数
lzy_vector(const lzy_vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end()); //利用了迭代器模板
swap(tmp);
}
3. 在实现拷贝构造后,实现赋值重载就比较简单了,利用传值拷贝构造的临时对象即可,然后调用swap类成员函数即可完成自定义类型的赋值工作。为了符合连续赋值含义,我们利用引用来作为返回值。
lzy_vector<T>& operator=(lzy_vector<T> v) //复用拷贝构造,存在自我赋值的问题,但不影响程序正确性
{
swap(v);
return *this;
}
注意:参数不可以加引用,正常来说,v1=v2; 如果加上引用的话,v2就会变成v1的值
总结:由于拷贝构造必须有引用,所以函数体内另外找了一个打工人,但我们的赋值不需要,所以引用和tmp必须同时存在,这样才可以满足我们的逻辑
三、vector的基本接口
3.1empty和clear
empty
bool empty() const
{
return _finish == _start;
}
clear
void clear()
{
_finish = _start;//这里可不是置为nullptr哦
}
3.2 size和capacity
size
size_t size() const
{
return _finish - _start;
}
capacity
size_t capacity() const
{
return _endofstorage - _start;
}
3.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;
}
}
reserve
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;
}
}
五、尾插尾删
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;
}
六、迭代器失效
6.1 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;
}
6.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;
}
七、vector.h
#include<iostream>
using namespace std;
namespace lzy
{
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;
}
}
希望对大家有所帮助!