vector的模拟实现
1. 成员变量
在了解了vector的的底层及基操之后,我们知道,vector是表示可变大小数组的序列容器,而其在底层维护着三个原生态类型的指针(start, finish,end_of_storage),而对vector进行模拟实现,其实说白了就是去使用和维护这三个指针。
namespace my_vector //使用自定义的命名空间,防止跟标准库中的vector起冲突
{
template<class T> //用模板的方式实现,因为vector可以存放任意类型的元素
class vector{
public:
typedef T* iterator; //迭代器,本质是T*类型的指针
private:
iterator start; //指向第一个元素所在的位置
iterator finish; //指向最后一个元素的下一个位置
iterator end_of_storage; //指向整个空间的末尾
};
}
2. 构造和析构(constructor and destructor)
1.1 构造
1.1.1 空的构造
//将指针全部初始化为nullptr即可
vector()
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{}
1.1.2 用n个T类型的元素来构造
注意: 用这个方式进行构造需要注意n的元素类型,如果只给一个size_t类型的 n 的构造方法,形如my_vector::vector v(10, 5); 这样的传参会被当做俩个int类型的参数从而不会调用这个函数,而是去调用下面区间构造的函数导致出错!
解决方案: 提供成对的该拷贝构造函数。
//n的类型为size_t
vector(size_t n, const T &value = T())
: start(new T[n])
, finish(start + n)
, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
{
//遍历赋值即可
for (int i = 0; i < n; i++) {
start[i] = value;
}
}
//n的类型为int
vector(int n, const T &value = T())
: start(new T[n])
, finish(start + n)
, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
{
for (int i = 0; i < n; i++) {
start[i] = value;
}
}
1.1.3 拷贝构造
//拷贝构造
vector(const vector<T> &v)
{
start = new T[v.capacity()];
finish = start + v.size();
end_of_storage = start + v.capacity();
for (size_t i = 0; i < v.size(); i++) {
start[i] = v[i];
}
}
1.1.4 区间构造
这里需要注意:区间构造所使用的的迭代器应该用模板来重新声明,因为如果使用iterator做迭代器,会导致初始化的迭代器区间[begin,end)只能是vector的迭代器,重新声明迭代器,迭代器区间[begin,end)可以是任意容器的迭代器。
//区间构造,需要使用模板的方式实现
template <class Iterator>
vector(Iterator begin, Iterator end)
{
int size = end - begin; //区间的元素个数
start = new T[size];
finish = start;
end_of_storage = start + size;
auto it = begin;
while (it != end) {
*finish++ = *it++;
}
}
1.2 析构
//析构
~vector()
{
if (start) {
//释放空间并将指针置为空
delete[] start;
start = finish = end_of_storage = nullptr;
}
}
3. 赋值运算符的重载(operator=)
利用后面实现的swap方法来简单实现赋值运算符的重载, 需要注意的是这里传参必须是以传值的方式进行,如果传递引用会修改右操作数,不符合赋值运算符的本意。
//赋值运算符的重载
vector<T>& operator=(vector<T> v) //注意这里是传值而不是传引用
{
swap(v); //利用swap将v赋给当前对象
return *this;
}
4. 迭代器相关(Iterators)
正向迭代器与反向迭代器所在的位置刚好相反。
4.1 正向迭代器
//正向迭代器
iterator begin()
{
return start;
}
iterator end()
{
return finish;
}
4.2 反向迭代器
//反向迭代器
iterator rbegin()
{
return finish;
}
iterator rend()
{
return start;
}
5. 容量相关(Capacity)
5.1 获取容量相关信息
知识点:指针 - 指针 = 二者之所经历间的元素个数
//有效元素个数
size_t size()const
{
return finish - start;
}
//容量大小
size_t capacity()const
{
return end_of_storage - start;
}
//判空
bool empty()const
{
return start == finish;
}
5.2 更改有效元素个数
//更改有效元素的个数
void resize(size_t newsize, const T &value = T())
{
size_t oldsize = size();
if (newsize > size()) { //说明有效元素个数增加
if (newsize > capacity()) { //需要扩容
reserve(newsize); //reserve下边实现
}
for (size_t i = oldsize; i < newsize; i++) {
start[i] = value; //填充后续元素
}
}
//不论是有效元素个数增加还是减少,最终都需对finsih进行修改
finish = start + newsize;
}
5.3 扩容
扩容需要稍微注意的是,不能无脑的直接对齐进行二倍或数倍的扩容,必须考虑刚开始容量为0的这种特殊情况,如果统一以倍数形式增长则这种情况无法扩容成功。
//扩容
void reserve(size_t newcapacity)
{
size_t oldcapacity = capacity();
//当新容量大于当前容量时才会真正扩容,否则什么都不做
if (newcapacity > oldcapacity) {
//开辟新空间
T *tmp = new T[newcapacity];
int n = size(); //记录当前的有效元素个数
//这个判断不能少,否则如果是空的vector,delete nullptr可能会出问题
if (start) {
//拷贝元素
memcpy(tmp, start, sizeof(T)* oldcapacity);
//释放旧空间
delete[] start;
}
//更新指针
start = tmp;
//这里的n不能是size(),因为旧空间已经被释放,此时start已经指向新的空间
//再使用size()方法去求原来空间的元素个数就可能会有问题
//这也是需要在一开始记录size()的原因
finish = start + n;
end_of_storage = start + newcapacity;
}
}
6. 元素访问相关(Element access)
这里有一点需要注意:这些方法的实现必须成对给出,分为const类型和非const类型的方法,因为const类型的对象无法调用非const类型的方法。
6.1 下标运算符的重载
//下标运算符的重载,注意需成对实现
T& operator[](size_t pos)
{
assert(pos < size());
return start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return start[pos];
}
6.2 获取头部 & 尾部元素
//头部元素及尾部元素,同样需要成对给出
T& front()
{
return *start;
}
const T& front()const
{
return *start;
}
T& back()
{
return *(finish - 1); //不忘忘记-1!!!
}
const T& back()const
{
return *(finish - 1);
}
7. 元素修改相关(Modifiers)
7.1 尾部插入 & 删除
//尾部插入
void push_back(const T &value) {
if (size() == capacity()) { //此时插入元素需要扩容
if (capacity() == 0) {
//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
reserve(1);
}
else {
reserve(capacity() * 2); //以二倍的方式进行扩容
}
}
*finish++ = value;
}
//尾部删除
void pop_back()
{
if (empty()) {
return;
}
--finish;
}
7.2 任意位置的插入 & 删除
//任意位置的插入
iterator insert(iterator pos, const T &value)
{
//扩容处理同上
if (size() == capacity()) { //此时插入元素需要扩容
if (capacity() == 0) {
//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
reserve(1);
}
else {
reserve(capacity() * 2); //以二倍的方式进行扩容
}
}
auto it = end();
while (it != pos) {
//pos之后的元素需整体向后搬移
*it = *(it - 1);
--it;
}
//插入元素并更新finish
*pos = value;
finish++;
return pos;
}
//任意位置的删除
iterator erase(iterator pos)
{
iterator next = pos + 1; //pos的下一个位置
while (next != finish) {
*(next - 1) = *next; //数据向前移动
next++;
}
finish--;//更新finish
return pos;
}
7.3 区间删除 & clear
//区间删除
iterator erase(iterator begin, iterator end)
{
finish = start;
return finish;
}
//清空vector
void clear()
{
//调用erase删除整个区间即可
erase(begin(), end());
}
7.4 交换操作
最开始的赋值运算符的重载便是调用该swap方法来完成。
//交换操作
void swap(vector<T> &v)
{
std::swap(start, v.start);
std::swap(finish, v.finish);
std::swap(end_of_storage, v.end_of_storage);
}
8. 完整代码
实现在自定义的头文件vecter.h中:
#ifndef _VECTOR_H_ //防止头文件被重复包含
#define _VECTOR_H_
#include <iostream>
#include <assert.h>
using namespace std;
namespace my_vector //使用自定义的命名空间,防止跟标准库中的vector起冲突
{
template<class T> //用模板的方式实现,因为vector可以存放任意类型的元素
class vector{
public:
typedef T* iterator; //迭代器,本质是T*类型的指针
public:
//空的构造
vector()
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{}
//用n个T类型的元素来构造
//n的类型为size_t
vector(size_t n, const T &value = T())
: start(new T[n])
, finish(start + n)
, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
{
for (int i = 0; i < n; i++) {
start[i] = value;
}
}
//n的类型为int
vector(int n, const T &value = T())
: start(new T[n])
, finish(start + n)
, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
{
for (int i = 0; i < n; i++) {
start[i] = value;
}
}
//区间构造,需要使用模板
template <class Iterator>
vector(Iterator begin, Iterator end)
{
int size = end - begin; //区间的元素个数
start = new T[size];
finish = start;
end_of_storage = start + size;
auto it = begin;
while (it != end) {
*finish++ = *it++;
}
}
//拷贝构造
vector(const vector<T> &v)
{
start = new T[v.capacity()];
finish = start + v.size();
end_of_storage = start + v.capacity();
for (size_t i = 0; i < v.size(); i++) {
start[i] = v[i];
}
}
//赋值运算符的重载
vector<T>& operator=(vector<T> v) //注意这里是传值而不是传引用
{
swap(v); //利用swap将v赋给当前对象
return *this;
}
//迭代器相关
//正向迭代器
iterator begin()
{
return start;
}
iterator end()
{
return finish;
}
//反向迭代器
iterator rbegin()
{
return finish;
}
iterator rend()
{
return start;
}
//容量相关
//有效元素个数
size_t size()const
{
return finish - start;
}
//容量大小
size_t capacity()const
{
return end_of_storage - start;
}
//判空
bool empty()const
{
return start == finish;
}
//更改有效元素的个数
void resize(size_t newsize, const T &value = T())
{
size_t oldsize = size();
if (newsize > size()) { //说明有效元素个数增加
if (newsize > capacity()) { //需要扩容
reserve(newsize); //reserve后边实现
}
for (size_t i = oldsize; i < newsize; i++) {
start[i] = value; //填充后续元素
}
}
//不论是有效元素个数增加还是减少,最终都需对finsih进行修改
finish = start + newsize;
}
//扩容
void reserve(size_t newcapacity)
{
size_t oldcapacity = capacity();
//当新容量大于当前容量时才会真正扩容,否则什么都不做
if (newcapacity > oldcapacity) {
//开辟新空间
T *tmp = new T[newcapacity];
int n = size(); //记录当前的有效元素个数
//这个判断不能少,否则如果是空的vector,delete nullptr可能会出问题
if (start) {
//拷贝元素
memcpy(tmp, start, sizeof(T) * oldcapacity);
//释放旧空间
delete[] start;
}
//更新指针
start = tmp;
//这里的n不能是size(),因为旧空间已经被释放,此时start已经指向新的空间
//再使用size()方法去求原来空间的元素个数就可能会有问题
//这也是需要在一开始记录size()的原因
finish = start + n;
end_of_storage = start + newcapacity;
}
}
//元素访问相关
//下标运算符的重载,注意需成对实现
T& operator[](size_t pos)
{
assert(pos < size());
return start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return start[pos];
}
//头部元素及尾部元素,同样需要成对给出
T& front()
{
return *start;
}
const T& front()const
{
return *start;
}
T& back()
{
return *(finish - 1); //不忘忘记-1!!!
}
const T& back()const
{
return *(finish - 1);
}
//元素修改相关
//尾部插入
void push_back(const T &value) {
if (size() == capacity()) { //此时插入元素需要扩容
if (capacity() == 0) {
//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
reserve(1);
}
else {
reserve(capacity() * 2); //以二倍的方式进行扩容
}
}
*finish++ = value;
}
//尾部删除
void pop_back()
{
if (empty()) {
return;
}
--finish;
}
//任意位置的插入
iterator insert(iterator pos, const T &value)
{
//扩容处理同上
if (size() == capacity()) { //此时插入元素需要扩容
if (capacity() == 0) {
//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
reserve(1);
}
else {
reserve(capacity() * 2); //以二倍的方式进行扩容
}
}
auto it = end();
while (it != pos) {
//pos之后的元素需整体向后搬移
*it = *(it - 1);
--it;
}
//插入元素并更新finish
*pos = value;
finish++;
return pos;
}
//任意位置的删除
iterator erase(iterator pos)
{
iterator next = pos + 1; //pos的下一个位置
while (next != finish) {
*(next - 1) = *next; //数据向前移动
next++;
}
finish--;//更新finish
return pos;
}
//区间删除
iterator erase(iterator begin, iterator end)
{
finish = start;
return finish;
}
//清空vector
void clear()
{
//调用erase删除整个区间即可
erase(begin(), end());
}
//交换操作
void swap(vector<T> &v)
{
std::swap(start, v.start);
std::swap(finish, v.finish);
std::swap(end_of_storage, v.end_of_storage);
}
//析构
~vector()
{
if (start) {
//释放空间并将指针置为空
delete[] start;
start = finish = end_of_storage = nullptr;
}
}
private:
iterator start; //指向第一个元素所在的位置
iterator finish; //指向最后一个元素的下一个位置
iterator end_of_storage; //指向整个空间的末尾
};
}
#endif /* _VECTOR_H_ */
听说帅的人看完都会顺手点赞的哦~