网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[first,last) 左闭右开的方式。
//[]
for (size_t i = 0; i < v.size(); i++) {
v1[i] += 1;
}
//iterator
vector<int>::iterator it = v.begin();
while (it != v.end()) {
cout << \*it << " ";
it++;
}
for (auto e : v) {
cout << e << " ";
}
1.4 修改操作
接口声明 | 解释 |
---|---|
void push_back (const value_type& val) | 尾插 |
void pop_back() | 尾删 |
iterator insert (iterator pos, const value_type& val) | 迭代器位置插入 |
void insert (iterator pos, size_type n, const value_type& val); | 迭代器位置插入 |
void insert (iterator pos, InputIter first, InputIter last) | 迭代器位置插入一段区间 |
iterator erase (iterator pos) | 迭代器位置删除 |
iterator erase (iterator first, iterator last) | 删除一段迭代器区间 |
void assign (size_type n, const value_type& val) | 覆盖数据 |
v.insert(ret, 30);
v.insert(ret, 2, 30);
v.insert(ret, v2.begin(), v2.end());
v1.erase(pos);
v1.erase(v1.begin(), v1.end());
#include <algorithm>
// 查找接口
template <class InputIter, class T>
InputIter find (InputIter first, InputIter last, const T& val);
2. vector的模拟实现
2.1 类的定义
template <class T, class Alloc = alloc>
class vector {
public:
typedef T\* iterator;
// ...
private:
iterator start;
iterator finish;gggg
iterator end_of_storage;
}
这个结构和顺序表结构稍有不同,但本质是一样的。只是将容量和元素个数的变量用指向对应位置的迭代器代替。
class Seqlist {
T\* _a; /\* start \*/
size_t _size; /\* finish - start \*/
size_t _capacity; /\* end\_of\_storage - start \*/
}
2.2 默认成员函数
//default constructor
vector()
: \_start(nullptr)
, \_finish(nullptr)
, \_end\_of\_storage(nullptr)
{}
//fill constructor
vector(size_t n, const T& val = T()) // 引用临时对象可延长其声明周期
: \_start(nullptr)
, \_finish(nullptr)
, \_end\_of\_storage(nullptr)
{
resize(n, val);
}
//copy constructor
vector(const vector<T>& v)
: \_start(nullptr)
, \_finish(nullptr)
, \_end\_of\_storage(nullptr)
{
_start = new T[v.capacity()];
for (size_t i = 0; i < v.capacity(); i++)
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
//range constructor
template <class InputIterator>
vector(InputIterator first, InputIterator last)
: \_start(nullptr)
, \_finish(nullptr)
, \_end\_of\_storage(nullptr)
{
while (first != last)
{
push\_back(\*first++);
}
}
//destructor
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
// 现代写法
//copy constructor
vector(const vector<T>& v)
: \_start(nullptr)
, \_finish(nullptr)
, \_end\_of\_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
//operator=
vector<T>& operator=(vector<T> v) /\* pass by value \*/
{
swap(v);
return \*this;
}
从范围构造可以看出类模板中的函数也可以是函数模板。
迭代器的分类
函数模板的模板参数要传迭代器区间时,命名是有规定的,范围构造中的InputIterator
就是一种指定的迭代器类型。因容器的结构各有不同,迭代器分为五种类型:
迭代器类型 | 名称 | 解释 | 适用容器 |
---|---|---|---|
input/output_iterator | 输入/输出迭代器 | 只读迭代器只能读取,只写迭代器可以写入该位置的值 | 无实际容器 |
forward_iterator | 向前迭代器 | 只能向前移动(++),允许在迭代器位置进行读写 | forward_list |
bidirectional_iterator | 双向迭代器 | 可以双向移动(++,––),允许在迭代器位置进行读写 | list, map, set |
random_access_iterator | 随机迭代器 | 支持指针运算,可移动(++,––)任意跳转(+,–)读写(*) | deque,vector,string |
可以看出,下方的迭代器类型是上方的父类,也就是说下方迭代器满足上方的所有要求。
划分出不同的迭代器类型,是为了限制传入的迭代器,因为其必须满足要求才能完成接下来的函数。
函数如果指明迭代器类型为InputIterator
,意思是满足输入迭代器的要求的迭代器都可以作此参数。故此处我们可以传入任意的迭代器。
一般底层是数组连续空间的容器,例如 vector, string 等都是随机迭代器。
像双向链表这样的非连续空间的容器是双向迭代器。
2.3 容量接口
memcpy 浅拷贝问题
vector<string> v;
v.push\_back("11111111111111");
v.push\_back("11111111111111");
v.push\_back("11111111111111");
v.push\_back("11111111111111");
v.push\_back("11111111111111"); // 增容浅拷贝
出现问题是因为正好数组需要增容。模拟实现的reserve
函数使用memcpy
将原空间的内容按字节拷贝至新空间。
- 若 vector 存储的是内置类型,则浅拷贝没问题。
- 若 vector 存储的是自定义类型,浅拷贝使得新旧变量指向同一块空间。深拷贝调用拷贝构造或者赋值重载。
void reserve(size_t n)
{
if (n > capacity())
{
T\* tmp = new T[n];
size_t oldSize = size();
if (_start)
{
//memcpy(tmp, \_start, size() \* sizeof(T)); // err
for (int i = 0; i < size(); i++)
{
tmp[i] = _start[i];//\_start指向的空间存任意类型都能完成深拷贝
}
delete[] _sta rt;
}
_start = tmp;
_finish = _start + oldSize;
_end_of_storage = _start + n;
}
}
void resize(size_t n, T val = T())
{
if (n > size())
{
reserve(n);
while (_finish != _start + n)
{
\*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
2.4 修改接口
iterator insert(iterator pos, const T& val)
{
assert(_start <= pos && pos <= _finish); // 检查pos位置是否合法
// 增容
if (_finish == _end_of_storage)
{
size_t sz = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() \* 2); //增容会导致迭代器失效,迭代器位置陈旧
pos = _start + sz; //增容后更新pos
}
// 后移 [pos,\_finish)
for (iterator end = _finish; end > pos; --end)
{
\*end = \*(end - 1);
![img](https://img-blog.csdnimg.cn/img_convert/f78ffb01b8bd15585ee5564702fb3e99.png)
![img](https://img-blog.csdnimg.cn/img_convert/469c92a7f8ea96a2f7892552c07ddaa0.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
nd - 1);
[外链图片转存中...(img-EwGp0Vt1-1715713639431)]
[外链图片转存中...(img-VypUcRPY-1715713639431)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**