🌈欢迎来到C++专栏~~vector的模拟实现
- (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort🎓
- 🌍博客主页:张小姐的猫~江湖背景
- 快上车🚘,握好方向盘跟我有一起打天下嘞!
- 送给自己的一句鸡汤🤔:
- 🔥真正的大师永远怀着一颗学徒的心
- 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
- 🎉🎉欢迎持续关注!


本文章按照逻辑链来完成vector的模拟实现,重点介绍迭代器失效问题,文末赋上源码.h
开始前,翻阅源码得知:
template<class T, class Alloc = alloc>
class vector
{
public:
typedef T* iterator;
private:
iterator start;
iterator finish;
iterator end_of_storage;
};
其实类似于我们之前的_a、_size、_capacity ~

_size = _finish - _start
_capacity = _end_of_storage - _start
类似顺序表,初始化都给空,不然push_back就会崩
//无参构造
vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
🧡析构函数
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
[]_start 原生指针,可以用下标访问
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
普通迭代器 & const迭代器,老生常谈的问题
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
迭代器支持, 范围for同样也能实现
插入数据要考虑扩容问题
🌊resize

void resize(size_t n, const T& val = T() )//0初始化语法
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
//初始化填值
while (_finish < _start + n)
{
*finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
注意:
匿名对象具有常性,不可更改,所以加const
resize默认填充的是T类型的缺省值,int类型就是0;指针类型就是空指针;若是自定义类型,比如vector,那么就是调用的vector的构造函数,构造vector的匿名对象,是零初始化的语法, x默认值为对应类型的0值
C++为了迎合模板需要,内置类型int等被认为有构造函数

🌊reserve
同样需要深拷贝,mencpy是浅拷贝
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
size_t sz = size();
//若原来已有空间,则需要释放旧空间、拷贝数据
if (_start)
{
//memcpy(tmp, _start, sizeof(T)*sz);
//delete[] _start;
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
注意:
sz保存起来// 错误示范,请勿模仿
_start = tmp;
_finish = _start + size();
_endofstorage = _start + n;
size()是_finish - _start(0),况且_start已经更新了,减完有可能就是负数了
拷贝数据不能简单地memcpy ,这样虽然vector是深拷贝的,比如vector,其中的vector仍然是浅拷贝的。对于int没有问题,但是对于自定义类型就出问题了。因此我们需要更改 ——
⚡push_back
//1、&引用防止深拷贝 2、+const可以右值引用,防止不能引用是常性的临时变量
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 :capacity()*2);
}
*_finish = x;
_finish++;
}
⚡pop_back
void pop_back()
{
assert(_finish > _start);
_finish--;
}
💦迭代器区间构造
//写成模板 :可以用其他迭代器区间进行构造
template <class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
while(first != last)
{
push_back(*first);
++first;
}
}
💢注意:
InputIterator的含义:函数模板的模板参数传迭代器区间是有命名规范的
这个后面会讲解到
💦拷贝构造——多种写法

void swap(const T& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
//现代写法2 : v2(v1) 资本家思维
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());//打工人
swap(tmp);//交换
}
nullptr,不然free的就是一块随机值的空间了💦赋值重载——现代写法
//赋值重载 v1 = v2
vector<T> operator= (vector<T> v)
{
swap(v);//
return *this;
}
这里是传值调用,v是v2拷贝构造来的。到这里我都忘了为什么拷贝构造不能传值必须传引用传送门🎉🎉:类和对象,因为拷贝构造要传参,传参完又是拷贝构造,会无限递归
🐋检查空间 —— 挪动数据 ——插入数据。 测试代码,发现insert插入第5个数据(即扩容时),出现了随机值

//错误示范
void insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= finish);//=就是尾插
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
//挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *(end);
--end;
}
*pos = x;
++_finish;
}
测试代码 ——
void test_vector2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
auto p = find(v.begin(), v.end(), 3);
if (p != v.end())
{
v.insert(p, 30);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
出现随机值

因为有一个隐藏的bug:如果insert时发生了扩容,会导致it指向的空间被释放,此时it还指向着已被释放的空间是野指针,这种问题,我们就叫做迭代器失效

💗于是,我们通过增容后,提前算出相对位置,更新pos来解决。此时外边的p仍然是失效的,则通过传返回值(An iterator that points to the first of the newly inserted elements.),若之后还需要使用p,再根据需要接收返回值(不能传引用,传引用会引发其他问题:返回常量)
🎉最终版:
void insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);//=就是尾插
if (_finish == _end_of_storage)
{
//扩容会导致迭代器失效,需要提前计算更新pos
size_t len = pos - _start;//提前算出相对位置
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
//挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *(end);
--end;
}
*pos = x;
++_finish;
}
同样测试得:
//测试insert - 迭代器失效问题
void testVector4()
{
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(), 2);
if (p != v.end())
{
// 在it位置插入数据后,不要在访问p,因为p可能失效了
p = v.insert(it, 20);
// 若insert时发生了扩容,会导致it指向的空间被释放
// it本质上是野指针,这种问题,我们就叫做迭代器失效
}
v.insert(p, 10);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}

常规思路:挪动数据覆盖。此处没有新开辟空间,也就没有野指针数据
// 错误示范,请勿模仿
void erase(iterator pos);
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--finish;
}
erase缩容也可能到时pos失效(具体看编译器实现)
现在,要求实现删除v中所有偶数,测试代码
//测试erase - 迭代器失效问题2
void test_vector4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//要求删除所有的偶数
auto 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 5 -> 正常(误打误撞)
1 2 4 5 -> 没删干净
1 2 3 4 -> 崩溃

it和_finish擦边而过了,*it就不断越界访问了,肯定崩溃总结,erase(it)后,it指向的位置意义已经变了,直接++,会导致一些意料之外的结果,这也是迭代器失效的问题。
💦我们翻阅文档得知:函数调用后会返回刚删除位置的下一个位置,即这个意义已经改变的pos(An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.)(我网易翻译的应该没错)
💗终极版erase
//stl规定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;//此时移动完pos就是被删的后一个数据
}
继续测试代码~
void test_vector4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
//要求删除所有的偶数
auto 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位置;一定要更新,若还需要访问,可先接收返回值更新假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,以下代码会发生什么问题?
int main()
{
bite::vector<bite::string> v;
v.push_back("1111");
v.push_back("2222");
v.push_back("3333");
return 0;
}


对于内置类型可以用mencpy,对于自定义类型就不能用mencpy了,memcpy是浅拷贝,指向同一块空间,要是析构or一个对象修改数据,就出问题了,就需要一个一个对象进行深拷贝赋值。
调用函数

vector.h#pragma once
#include
#include
#include
using namespace std;
#include
namespace ljj
{
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()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
//写成模板 :可以用其他迭代器区间进行构造
template <class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
while(first != last)
{
push_back(*first);
++first;
}
}
//拷贝构造 ~ 传统写法
//v1(v)
//vector(const vector& v)
//{
// _start = new T[v.size()];
// //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();
// _end_of_storage = _start + v.size();
//}
//现代写法1 : v2(v1)
//vector(const vector& v)
// :_start(nullptr)
// ,_finish(nullptr)
// ,_end_of_storage(nullptr)
//{
// reserve(v.size());
// for (const auto& e : v)
// {
// push_back(e);
// }
//}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
//现代写法2 : v2(v1) 资本家思维
vector(const vector<T> & v)
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());//打工人
swap(tmp);//交换
}
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);
}
}
//赋值重载 v1 = v2
vector<T> operator= (vector<T> v)
{
swap(v);//
return *this;
}
T& front()
{
assert(size() > 0);
return *_start;
}
T& back()
{
assert(size() > 0);
return *(finish - 1);
}
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
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;
}
}
void reserve(size_t n)
{
//大于才扩容
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
void push_back(const T& x)//1、&引用防止深拷贝 2、+const可以右值引用,防止不能引用常性的临时变量
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
void pop_back()
{
assert(_finish > _start);
_finish--;
}
iterator insert(iterator pos, const T& x)
{
assert(pos > _start);
assert(pos <= _finish);//=就是尾插
if (_finish == _end_of_storage)
{
size_t len = pos - _start;//提前算出相对位置
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
//挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *(end);
--end;
}
*pos = x;
++_finish;
return pos;
}
//stl规定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;
//if (size() < capacity() / 2)
//{
// //缩容 --以时间换空间
//}
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
void test_vector1()
{
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 = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto ch : v)//傻瓜式的替换 begin ——》Begin都会报错
{
cout << ch << " ";
}
cout << endl;
}
//测试insert - 迭代器失效问题
void test_vector2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(200);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
auto p = find(v.begin(), v.end(), 3);
if (p != v.end())
{
v.insert(p, 30);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
//测试erase - 迭代器失效问题
void test_vector3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
auto p = find(v.begin(), v.end(), 3);
if (p != v.end())
{
v.erase(p);
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
//测试erase - 迭代器失效问题2
void test_vector4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
//要求删除所有的偶数
auto 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 test_vector5()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
vector<int> v1(v);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
//迭代器区间构造
void test_vector6()
{
string s("hello world");
vector<int> v(s.begin(), s.end());
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector7()
{
vector<int*> v1(10);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
//int int
//vector v2(10,10);//编译报错:非法的间接寻址 语法问题
//for (auto e : v2)
//{
// cout << e << " ";
//}
//cout << endl;
// char int
vector<char> v3(10, 'a');
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
}
//测试resize
void test_vector8()
{
vector<int> v1;
v1.resize(10, 0);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vector<int> v2;
v2.reserve(10);
v2.push_back(1);
v2.push_back(2);
v2.push_back(3);
v2.push_back(4);
v2.resize(8,8);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
v2.resize(20,20);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
v2.resize(3);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
}
}
test.c#include"vector.h"
int main()
{
//ljj::test_vector1();
//ljj::test_vector2();
//ljj::test_vector3();
//ljj::test_vector4();
//ljj::test_vector5();
//ljj::test_vector6();
//ljj::test_vector7();
ljj::test_vector8();
return 0;
}
