一,引言
vector的模拟实现旨在还原其核心功能,通过深入理解底层架构来提升对标准库vector的掌握程度。vector的实现由类模板实现。其中核心部分是由一个顺序表和三个指针组成:1._start
指向顺序表的起始位置2._finish
指向有效元素的下一个位置3._end_of_storage
指向容器容量的下一个位置迭代器直接使用原生指针表示。首先需要搭建基础框架。如下:
namespace Cao
{
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
private:
iterator _stare = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
}
其中将vector类包含在Cao的命名空间是为了个标准库中的vector做区分。也可以更好的对比自己写的模拟vector和标准库中给的vector的不同。下文中的所有成员函数都在vector这个类进行实现。接下来还需要写一个打印函数用来呈现模拟vector的运行结果。如下:
template<class T>
void Printf_Container(const vector<T>& v)
{
auto it = v.begin();
for (it; it < v.end(); it++)
{
std::cout << (*it);
}
std::cout << endl;
for (auto ch : v)
{
std::cout << ch << ' ';
}
std::cout << endl;
}
打印函数写成模板的形式,可以打印各种数据类型。
二,成员函数
1,size
首先确定元素个数,实现逻辑如下:
size_t size()const
{
return _finish - _stare;
}
指针相减返回元素个数。
2,capacity
函数实现vector容器容量的大小,实现逻辑如下:
size_t capacity()const
{
return _end_of_storage - _stare;
}
指针相减返回容量大小
3,reserve
对vector容器进行扩容,实现逻辑如下:
void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
iterator tmp = new T[n];
for (int i = 0; i < old_size; i++)
{
tmp[i] = _str[i];
}
delete[]_stare;
_stare = tmp;
_finish = _stare + old_size;
_end_of_storage = _stare + n;
}
}
当需要扩容的值超过当前 vector 容量时进行扩容,否则无需处理。提前记录 size() 是因为扩容后原内存空间会被释放。最后更新 vector 的三个成员变量。
打印函数和上文的三个函数相对于准备工作。之后就可以插入数据等等一系列函数实现:
4,push_back
在vector数据末尾插入数据。实现如下:
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4:capacity() * 2);
}
*_finish = x;
_finish++;
}
首先进行判满,如果满了需要扩容,之后再_finish的位置添加一个元素。
5,pop_back
删除最后一个数据。实现如下:
void pop_back()
{
_finish--;
}
6,begin,end
返回第一个位置的迭代器,返回最后一个有效元素下一个位置的迭代器。实现如下:
iterator begin()const
{
return _stare;
}
iterator end()const
{
return _finish;
}
const_iterator begin()const
{
return _stare;
}
const_iterator end()const
{
return _finish;
}
对于const对象要引用const迭代器。否则会引起权限的放大。下文将会仔细写这个问题 。
7,构造函数
构造函数包括三种形式,具体见vector的讲解。实现如下:
第一种默认构造:
vector(const T& val = T())
{
if (val == T())
{
return;
}
reserve(1);
push_back(val);
}
当val传值的时候就开辟一个空间,否则不开空间。
第二种构造:
vector(size_t n, const T& val = T())
{
reserve(n);
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
n个val数据的构造。
第三种构造:
template <class inputiterator>
vector(inputiterator first, inputiterator last)
{
reserve(last - first);
while (first != last)
{
push_back(*first);
++first;
}
}
区间构造。注意类模板内部可以进行使用函数模板。
第四种拷贝构造:
vector(const vector<T>& val)
{
reserve(val.size());
for (auto& e : val)
{
push_back(e);
}
}
先经行开辟空间,之后一个一个进行拷贝。
8,swap
交换两个vector对象。具体实现如下:
void swap(vector<T>& v)
{
std::swap(_stare, v._stare);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
交换内部的三个成员变量,底层调用std标准库里面的交换函数。
9,运算符重载(=)
实现赋值操作。具体实现如下:
vector<T>& operator=( vector<T> v)
{
swap(v);
return *this;
}
这里的赋值不是传引用传参,所以说形参的改变不影响实参。
10,resize
具体的功能见上文vector解释。具体实现如下:
void resize(size_t n, T val = T())
{
if (n < size())
{
_finish = _stare + n;
}
while (n - size())
{
push_back(val);
}
}
size小于n就缩短长度,大于n就指定数值进行填充。
11,empty
判空,如若返回空就是true,否则是false。具体实现如下:
bool empty()
{
if (size() == 0)
{
return true;
}
return false;
}
12,运算符重载([])
返回指定位置的引用。具体实现如下:
T& operator[](size_t n)
{
return _stare[n];
}
const T& operator[](size_t n)const
{
return _stare[n];
}
第一种可以修改,第二种不可以修改。
13,assign
替换vector对象的数据。具体实现如下:
void assign(size_t n,const T& val = val())
{
clear();
while (n)
{
push_back(val);
n--;
}
}
14,clear
清除vector数据。具体实现如下:
void clear()
{
_finish = _stare;
}
15,insert
指定位置插入数据。具体实现如下:
iterator insert(iterator pos, const T& x)
{
// 扩容
if (_finish == _end_of_storage)
{
size_t len = pos - _stare;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _stare + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
主要分为两部分第一部分是判断有没有满。第二部分就移动数据。最后进行插入数据。
三,总结
在进行vector模拟实现中首先是关于权限的问题。加const和不加,有着权限缩小的影响。当用const变量调用非const变量会导致权限放大的错误。第二点关于迭代器失效的问题,下一篇文章会进行详细讲解。第三点是关于reserve深浅拷贝的问题,下一篇文章中也会单独出来讲解。有任何问题可以评论区留言或者私信都会一一解答。