C++基础:vector的底层实现
文章目录
- 1.类的创建与简单函数的实现
- 2.vector常用函数的实现
- 2.1 push_back
- 2.2 迭代器的实现
- 2.3 print_vector的实现(迭代器的进阶使用)
- 2.4 pop_back
- 2.4 insert(迭代器失效问题)
- 2.5 erase
- 2.6 resize
1.类的创建与简单函数的实现
类的创建:
这里我们使用了模版就不能将声明的定义分离了,会出现链接错误
简单函数的实现:
size_t size()
{
return _finish - _start;
}
size_t capacity()
{
return _end_of_storage - _start;
}
T& operator[](size_t i)//下标访问
{
assert(i < size());
return _start[i];
}
空间的扩容:
if (_finish != _end_of_storage)
{
*_finish = x;
++_finish;
}
else
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
*_finish = x;
++_finish;
}
当然也可以写成两个相同的内容放一起:
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
2.vector常用函数的实现
2.1 push_back
尾插的实现也很简单:
void push_back(const T& x)//加引用是因为遇到vector会进行拷贝
{
if (_finish != _end_of_storage)
{
*_finish = x;
++_finish;
}
else
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
*_finish = x;
++_finish;
}
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
但是现在运行程序却崩溃了,这是什么原因呢?
其原因是这里的对size()
的调用:
其中_finish
为扩容前的而_start
为扩容后的
这样进行相减会将新的_start
减去无法算到原有的值
更改方法:
- 先进行初始化
_finish
_finish = tmp + size();
_start = tmp;
_end_of_storage = _start + size();
- 将之前的
size()
数据留下来:
这样程序就正常了
2.2 迭代器的实现
迭代器和string
的实现方法一样
将begin
和end
函数写出来就可以实现了:
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
2.3 print_vector的实现(迭代器的进阶使用)
为了方便打印我们可以将函数的打印写成一个函数:
typedef const T* const_iterator;
iterator begin() const
{
return _start;
}
iterator end() const
{
return _finish;
}
void print_vector(const vector<int>& v)
{
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
我们还可以改创建一个模版让这个函数可以适合任何的数据类型
但是会遇到下面的问题:
template<class T>
void print_vector(const vector<T>& v)
{
//规定没有实例化的模板里取东西,编译器不能区分const_iterator是类型还是静态成员变量
typename vector<T>::const_iterator it = v.begin();
//auto it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
2.4 pop_back
这里先进行判断vector是否为空,不为空直接将_finish--
即可
bool empty()
{
return _finish == _start;
}
void pop_back()
{
assert(!empty());
--_finish;
}
2.4 insert(迭代器失效问题)
我们先来看看大体的实现:
void insert(iterator pos,const T& x)
{
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++;
}
这里一旦扩容迭代器就失效了
因为迭代器指向的空间被释放导致迭代器失效
我们可以用相对位置来修正这个pos的位置
那如果我们想先找一个数字再它的前面添加呢
我们先来了解一个库里的find
函数:
这个函数利用迭代器可以进行查找
那我们写一段代码试一下:
运行测试:
那如果还想对这个p
位置进行修改呢?
我们再来试一下:
这里就出问题了
为什么我们的p
指向的数字发生了变化,不在指向查找的数字了?
分析一下:
所以==insert以后p就失效了,不要进行访问==
2.5 erase
我们来实现一下这个函数的基本功能:
这里写一个简单的循环删除偶数来测一下:
但是会出现以下两种情况:
- 没删干净
原因我们来看一下:
- 越界访问了
越界的原因:
所以我们默认erase
后,迭代器就失效了
2.6 resize
先来看一看实现:
这里我们遇到了T val = T()
这里是用一个匿名对象来调用默认构造来进行函数的传参
当理解这个里面的实现很简单