文章目录
- 一、成员变量
- 二、常用迭代器接口模拟实现
- 三、一些常用接口模拟
- 四、默认成员函数
- 五、功能测试
一、成员变量
我们通过在堆上申请一个数组空间来进行储存数据,我们的成员变量是三个指针变量,分别指向第一个位置、最后储存有效位置的下一个位置以及数组空间的最后位置的下一个位置。
//为了与stl保持一致使用重命名
typedef T* iterator;
typedef const T* const_iterator;
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
二、常用迭代器接口模拟实现
直接返回数组的开始位置和有效数据的下一个位置即可。
//迭代器
//第一个位置
iterator begin()
{
return _start;
}
//有效数据的下一个位置
iterator end()
{
return _finish;
}
//重载 const
const iterator begin() const
{
return _start;
}
const iterator end() const
{
return _finish;
}
三、一些常用接口模拟
(1)size
求有效数据的个数,用结束位置下一个位置的指针减去第一个位置的指针就是有效数据的个数了。
size_t size() const
{
return _finish - _start;
}
(2)capacity
求容器的容量,用数组空间的最后位置的下一个位置的指针减去第一个位置的指针就是容量的大小了。
size_t capacity() const
{
return _end_of_storage - _start;
}
(3)重载[]
//重载 []
T operator[](size_t i)
{
//保证i位置符合
assert(i < size());
return *(_start + i);
}
const T operator[](size_t i) const
{
//保证i位置符合
assert(i < size());
return *(_start + i);
}
(4)reserve
预留空间,一般用于扩容,这里需要手动扩容,再拷贝数据,最后再改变成员变量的指向。
void reserve(size_t n)
{
//先保存有效数据个数
size_t _size = size();
//判断是否需要扩容
if (n > _size)
{
//手动申请空间
iterator tmp = new T[n];
//拷贝数据到新空间
for (size_t i = 0; i < _size; i++)
{
*(tmp + i) = *(_start + i);
}
//释放掉原来的空间
delete[] _start;
//重新改变位置
_start = tmp;
_finish = _start + _size;
_end_of_storage = _start + n;
}
}
注意:
a.要先保存 size
,因为在重新指向时需要_start
第一个重新指向新空间,如果此时再去调用size()
函数的话,返回的size
是不确定的。最终导致_finish
指向错误。
b.就是不能使用memcpy
函数进行拷贝,因为memcpy
是浅拷贝,当出现数据是自定义类型而且也是需要申请资源的话就会出现重复释放等问题。如数据储存的是string
类型。
所以需要使用深拷贝。
(5)clear
清空,将_start = _finish
即可。
void clear()
{
_finish = _start;
}
(6)insert
在迭代器位置前插入一个元素,并返回新的position
位置。
迭起器失效问题:
插入一个位置可能会扩容,扩容就会导致position
位置失效,所以如果需要扩容就先保存好 position
相对 _start
的位置。
iterator insert(iterator position, const T val)
{
//迭代器位置
assert(position >= _start);
assert(position <= _finish);
//空间是否够
if (_finish == _end_of_storage)
{
//保存相对位置
size_t _size = position - _start;
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
//重新获取position位置
position = _start + _size;
}
//往后移动一位
iterator end = _finish;
while (end > position)
{
*end = *(end-1);
--end;
}
*position = val;
++_finish;
return position;
}
(7)push_back
尾插入一个元素,这里直接调用insert即可。
void push_back(T x)
{
insert(_finish, x);
}
(8)erase
删除position
位置,返回position
位置的值。
这里也可能会存在迭代器失效问题,如缩容(这里不考虑)、最后一个位置。
//删除迭代器
iterator erase(iterator position)
{
//符合位置
assert(position >= _start);
assert(position < _finish);
//覆盖掉position
iterator end = position;
while (end < _finish - 1)
{
*end = *(end + 1);
++end;
}
--_finish;
//这里返回空,因为position已经失效了
if (position == _finish + 1)
{
return nullptr;
}
return position;
}
(9)empty
是否空。
bool empty() const
{
return size() == 0;
}
(10)pop_back
尾删,这里直接调用erase()
即可。
void pop_back()
{
if (empty())
return;
erase(_finish-1);
}
(11)resize
重新定义大小,n小于实际大小就缩小,n大于实际大小就用val来填充。
//重新定义大小
void resize(size_t n, T val = T())
{
if (n > capacity())
{
reserve(n);
}
size_t _size = size();
if (n > _size)
{
for (int i = _size; i < n; i++)
{
//直接尾插
push_back(val);
}
}
else
{
_finish = _start + n;
}
}
(12)swap
交换两个容器。
void swap(vector<T>& x)
{
//利用std库中的将指针位置交换即可
std::swap(_start, x._start);
std::swap(_finish, x._finish);
std::swap(_end_of_storage, x._end_of_storage);
}
四、默认成员函数
(1)默认构造
无参构造,给了有默认值,这里不用初始化。
vector() {};
用n个val来初始化
vector(size_t n, T val = T())
{
//先预留空间,这样就不用扩容了
reserve(n);
int i = 0;
while (i < n)
{
//尾插
push_back(val);
++i;
}
};
拷贝构造
这里要用深拷贝
vector(vector<T> &x)
{
//先预留空间,这样就不用扩容了
reserve(x.capacity());
//直接将获取的数据插入即可,不用memcpy这种直接拷贝
vector<T>::iterator it = x.begin();
while (it != x.end())
{
push_back(*it);
++it;
}
};
用迭代器区间构造,因为可以用其他的容器来初始化,所以这里再用一个模板。
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
//先预留空间,这样就不用扩容了
reserve(last - first);
while (first < last)
{
push_back(*first);
++first;
}
}
因为迭代器区间的构造函数与用n个val来初始化构造会冲突。
为了解决这个问题,使用一个具体一点的n个val来初始化的构造函数
vector(int n, T val = T())
{
reserve(n);
int i = 0;
while (i < n)
{
push_back(val);
++i;
}
};
(2)析构函数
~vector()
{
//释放
delete[]_start;
_start = _finish = _end_of_storage = nullptr;
}
(3)重载 =
vector<T>& operator=(vector<T> x)
{
//利用交换函数,x出了该函数作用域就会销毁,所以不用我们手动销毁原来空间了
swap(x);
return *this;
}
五、功能测试
(1)内置类型
void test01()
{
//n个val
xu::vector<int> v1(2,1);
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//区间
xu::vector<int> v2(v1.begin(), v1.end());
for (int i = 0; i < v1.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
//赋值
xu::vector<int> v3;
v3 = v1;
for (int i = 0; i < v1.size(); i++)
{
cout << v3[i] << " ";
}
cout << endl;
v1.push_back(2);
v1.insert(v1.begin(), 0);
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
v1.pop_back();
v1.erase(v1.begin());
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
v1.resize(10, 5);
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
}
(2)自定义类型
void test02()
{
//n个val
xu::vector<string> v1(2, "aa");
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//区间
xu::vector<string> v2(v1.begin(), v1.end());
for (int i = 0; i < v1.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
//赋值
xu::vector<string> v3;
v3 = v1;
for (int i = 0; i < v1.size(); i++)
{
cout << v3[i] << " ";
}
cout << endl;
v1.push_back("bb");
v1.insert(v1.begin(), "cc");
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
v1.pop_back();
v1.erase(v1.begin());
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
v1.resize(10, "ee");
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
}