目录
一、定义my_vector类
此处标明:以下成员函数都是写在my_list类内部的
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
class my_vector
{
public:
//构造函数
//赋值运算符重载
//析构函数
//迭代器
typedef T* iterator;
typedef const T* const_iterator;
//Capacity
//element access
//modify
private:
iterator _start; //首元素地址
iterator _finish; //最后一个元素下一个位置地址
iterator _end_of_storage; //空间尾地址
};
二、成员函数实现
(一)构造函数
1、无参构造
my_vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
2、n个value构造
my_vector(size_t n, const T& val = T())
{
//开空间
_start = new T[n];
//赋值
size_t i = 0;
while (i < n)
{
_start[i] = val;
i++;
}
_finish = _end_of_storage = _start + n;
}
3、迭代器构造
因为这里的迭代器是一个泛型的迭代器,有可能迭代器内部没有实现减法的功能,所以这里就通过迭代器遍历然后尾插的操作来实现迭代器构造
template<class InputIterator>
my_vector(InputIterator first, InputIterator last)
{
//first和last都是泛型迭代器,并不一定支持相减的操作,所以用迭代器遍历尾插操作构造
_start = new T;
while (first != last)
{
push_back(first);
first++;
}
}
构造实例:
void test()
{
int arr[5] = {1, 2, 3, 4, 5};
my_vector<int> v(arr, arr + 5);
}
(二)拷贝构造
my_vector(const my_vector<T>& v)
{
size_t sz = v.size();
size_t cap = v.capacity();
//开空间
T* tmp = new T[cap];
//深拷贝
size_t i = 0;
while (i < sz)
{
tmp[i] = v[i];
i++;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + v.capacity();
}
在将拷贝对象的内容拷贝到构造对象时,因为对象类型可能是自定义类型,所以不能使用memcpy来进行浅拷贝,而是要用循环赋值的方式进行深拷贝;
(三)赋值运算符重载
my_vector<T>& operator=(my_vector<T> v)
{
Swap(v);
return *this;
}
首先在接收参数时发生了一次拷贝构造,再将形参与对象本身进行交换,完成了一次深拷贝,函数结束时调用析构将形参析构,这样就完成了一次赋值;
(四)析构函数
~my_vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
}
(五)迭代器
//迭代器
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cbeign() const
{
return _start;
}
const_iterator cend() const
{
return _finish;
}
(六)容量相关
1、获取有效元素个数size
因为 _finish 指向最后一个元素的下一个位置,_start指向首元素位置,所以两个的差值就是有效元素个数;
size_t size() const
{
return _finish - _start;
}
2、获取最大有效元素容量capacity
同理,_end_of_storage指向空间末尾位置,所以与 _start 的差值就是空间的容量;
size_t capacity() const
{
return _end_of_storage - _start;
}
3、增加空间总容量reserve
在增容时,先判断新增的容量是否大于原有空间的容量,如果小于,则不进行增容;
如果大于则进行以下操作:
(1)开空间
(2)将原有空间内容拷贝到新空间
(3)销毁原有空间
(4)改变指针指向
//reserve
void reserve(size_t new_capacity)
{
if (new_capacity > capacity())
{
size_t sz = size();
//开空间
T* tmp = new T[new_capacity];
//深拷贝
size_t i = 0;
while (i < sz)
{
tmp[i] = _start[i];
i++;
}
//释放原有空间
delete[] _start;
//改变指向
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + new_capacity;
}
}
4、改变有效字符个数resize
改变有效字符个数分为以下几种情况:
(1)当newSize > size时,将新加的空间赋上传入的值;
(2)当newSize > capacity时,reserve增容,再赋值;
(3)当newSize < size时,直接改变_finish指针;
//resize
void resize(size_t new_size, const T& val = T())
{
if (new_size > size())
{
if (new_size > capacity())
{
reserve(new_size);
}
iterator it = end();
while (it != _start + new_size)
{
*it = val;
it++;
}
}
_finish = _start + new_size;
}
(七)Element Access
[]运算符重载
1、非const修饰的[]运算符重载函数
在获取index位置的值之前,需要判断边界,看是否越界访问;
非const修饰的可以访问,也可以修改内容;
T& operator[](size_t index)
{
//判断边界
assert(index < size());
return _start[index];
}
2、const修饰的[]运算符重载
const修饰的可以访问但是不能修改内容
const T& operator[](size_t index) const
{
//判断边界
assert(index < size());
return _start[index];
}
(八)修改modify
1、交换函数Swap
void Swap(my_vector<T>& v)
{
swap(_start, v._start);
swap(_finish, v._finish);
swap(_end_of_storage, v._end_of_storage);
}
2、任意位置前插入一个值insert
插入前首先要判断边界条件和是否需要扩容;
//任意位置前插入
iterator insert(iterator pos, const T& val)
{
//判断边界
assert(pos >= begin() && pos <= end());
//获取pos的偏移量
size_t len = pos - _start;
if (_finish == _end_of_storage)
{
size_t new_capacity = (_start == nullptr ? 1 : capacity() * 2);
reserve(new_capacity);
}
iterator it = end();
//更新pos的位置,防止增容之后pos失效
pos = _start + len;
while (it != pos)
{
*it = *(it - 1);
it--;
}
*pos = val;
_finish++;
return pos;
}
因为传入的位置是迭代器,所以当增容时,会使迭代器失效,那么就必须要更新pos的位置,防止增容之后pos迭代器失效导致出现错误;
返回值也是一个迭代器,返回的是pos位置的迭代器,指向的就是新加入的元素的位置,这也是防止在插入元素时迭代器失效的问题;
3、删除任意位置元素erase
//删除任意位置
iterator erase(iterator pos)
{
//判断边界
assert(pos >= begin() && pos < end());
iterator it = pos + 1;
while (it != end())
{
*(it - 1) = *it;
it++;
}
_finish--;
return pos;
}
同插入,删除的返回的也是pos位置的迭代器,指向的就是删除之前下一个元素的位置,这也是防止在插入元素时迭代器失效的问题;
4、尾插,尾删
尾插(push_back)、尾删(pop_back)都是insert和erase两个函数的复用;
//尾插
void push_back(const T& val)
{
insert(_finish, val);
}
//尾删
void pop_back()
{
erase(_finish - 1);
}