目录
一.前言
本文将通过模拟实现c++中的vector模板中一些常用的函数功能,具体包含push_back,reserve等函数的模拟,但并不考虑到所有的情况,因此并没有过多的函数重载。
本文共两部分,一部分用来实现vector,头文件命名vector.h,另一部分则为源码实现部分(编译器环境为VS2019)。各个函数的返回值类型和参数类型均与库中的vector一样。
二.模板函数功能实现
1.vector构建
vector类共有三个成员,不同于string类,因为vector需要适用各种模板类,且迭代器适用于所有模板,因此仅需要指向数据块的开始_start,指向有效数据的末尾_finish和指向存储容量的末尾_endOfStorage这三个迭代器即可。具体实现如下:
#pragma once
namespace Vector
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _endOfStorage; // 指向存储容量的尾
};
}
2.迭代器设置
因为vector类是数组,即连续存储的空间,可以直接通过加减等操作获得迭代器的位置。简单设置如下:
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
3.构造函数和析构函数
①构造函数
此次模拟的构造函数共有四种,分别为默认构造函数、传值构造函数、拷贝构造函数和迭代器构造函数。
//默认构造
vector()
:_start(nullptr)
, _finish(nullptr)
, _endOfStorage(nullptr)
{}
//传值构造
//此处n如果类型为size_t,会与下面迭代器构造函数引起int类型初始化的歧义
vector(int n, const T& value = T())
{
T* tmp = new T[n];
_start = tmp;
_finish = _endOfStorage = tmp + n;
for (size_t i = 0; i < n; ++i)
{
*tmp = value;
tmp++;
}
}
//迭代器构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
first++;
}
}
//拷贝构造
vector(const vector<T>& v)
{
size_t sz = v.size();
size_t cap = v.capacity();
T* tmp = new T[cap];
_start = tmp;
_finish = tmp + sz;
_endOfStorage = tmp + cap;
for (size_t i = 0; i < sz; ++i)
{
*tmp = v[i];
++tmp;
}
}
vector的构造函数只需要设置好迭代器就行,如果开辟了新的空间,将迭代器设置过去就行,如果需要拷贝,也可以直接使用二者的迭代器拷贝。其中,上述的size()等函数都将在下面实现。
②析构函数
析构函数很简单,由于vector是一段连续的数据块,直接释放掉起始位置即可,也就是迭代器初始位置_start。释放后将所有迭代器置空。
//析构
~vector()
{
if (_start)
{
delete[] _start;
_start = _endOfStorage = _finish = nullptr;
}
}
4.size函数(获取大小)
size_t size() const
{
return _finish - _start;
}
5.capacity函数(获取容量)
size_t capacity() const
{
return _endOfStorage - _start;
}
6.push_back函数(尾插)
因为是连续的数据块,每次插入需要考虑扩容的问题,这里我们选择二倍扩容的方式。
void push_back(const T& x)
{
if (_finish == _endOfStorage)
{
size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
reserve(new_cap);
}
*_finish = x;
_finish++;
}
7.reserve函数(扩容)
//扩容函数
void reserve(size_t n)
{
if (n > capacity())
{
//防止迭代器失效,先保存原大小
size_t Size = size();
//拷贝数据到新的数据块
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * Size);
delete[] _start;
}
//设置迭代器到新的数据块
_start = tmp;
_finish = tmp + Size;
_endOfStorage = tmp + n;
}
}
8.pop_back函数(尾删)
尾删函数只需要将指向最后的迭代器向前移动一位即可,不用考虑容量大小的问题。(删除数据不需要减容,多此一举)
void pop_back()
{
assert(_start);
if (size())
{
_finish--;
}
}
9.resize函数(重定义大小)
该函数除了需要考虑扩容,还需要对数据块的大小做改变,重定义的大小为n,对超过原大小的数据使用传递的类初始化。
//重定义大小
void resize(size_t n, const T& value = T())
{
reserve(n);
while (n > size())
{
*_finish = value;
_finish++;
}
_finish = _start + n;
}
10.swap函数(交换)
从结构上看,vector的三个迭代器都是指针,swap直接交换二者的迭代器就可以,因此参数传引用即可。交换可以直接使用std中的交换函数。
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
}
11.operator=(重载赋值)
因为前面有swap函数,要赋值的话直接把this指针指向类的迭代器与参数交换就可以了。但有个前提,参数类不能在函数结束时就被销毁,如果传引用则会改变原来的类,不适用;此时可以直接设置为传值调用——因为在调用时会对目标参数进行拷贝构造,此时的参数直接与*this交换即可。当然,如果使用传统的赋值也可以。
//赋值
vector<T>& operator=(vector<T> v)
{
//size_t cap = v.capacity();
//size_t sz = v.size();
//T* tmp = new T[cap];
//if (v.begin())
// memcpy(tmp, v._start, sizeof(T) * sz);
//_start = tmp;
//_finish = tmp + sz;
//_endOfStorage = tmp + cap;
swap(v);
return *this;
}
12.operator[](重载接收)
T& operator[](size_t pos)
{
return *(_start + pos);
}
const T& operator[](size_t pos) const
{
return *(_start + pos);
}
13.insert(插入)与erase(删除)函数
此处将二者放一起,因为二者都具有迭代器失效的可能性。
①insert
在指定迭代器位置插入一个数据。具有扩容的可能性,但是我们传入的迭代器仍然指向原来的位置,此时已经释放掉了,因此我们此时需要对传入的迭代器重新设置。
上述仅仅是解决了内部迭代器失效的问题,如果返回类型为void,外部的迭代器就会失效,无法继续访问,最好的解决办法就是返回更新后的迭代器,若要继续使用,请记住接收更新后的迭代器。
iterator insert(iterator pos, const T& x)
{
assert(pos >= begin() && pos <= end());
if (_finish == _endOfStorage)
{
//防止迭代器失效
size_t len = pos - begin();
size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
reserve(new_cap);
pos = begin() + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
_finish++;
*pos = x;
return pos + 1;
}
②erase
删除指定迭代器位置的数据。删除的操作步骤有两个,一、删除迭代器位置的数据;二、挪动删除后面的数据到前面一位。但是系统自带的迭代器并不会因此连续访问同一个迭代器——它会不断++,直到end()。这意味着又需要我们手动来调整了,将返回值仍然设置为pos位置即可。
倘若不返回pos位置,继续原本的迭代器,理论上是可行的,只会出现数据上的错误。其实不然,在不同的操作系统、环境下情况都不同,有的可以正常运行,而有的会报错,所以若要继续使用迭代器,一定要手动更新一下。
iterator erase(iterator pos)
{
assert(pos >= begin() && pos < end());
size_t beg = pos - begin();
size_t sz = size();
while (beg < sz)
{
_start[beg] = _start[beg + 1];
beg++;
}
_finish--;
return pos;
}
三.源码(包含部分测试用例)
#pragma once
#include <iostream>
#include <assert.h>
namespace Vector
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//构造
vector()
:_start(nullptr)
, _finish(nullptr)
, _endOfStorage(nullptr)
{}
//此处n如果类型为size_t,会与下面类构造函数引起int类型初始化的歧义
vector(int n, const T& value = T())
{
/*T* tmp = new T[n];
_start = tmp;
_finish = _endOfStorage = tmp + n;
for (size_t i = 0; i < n; ++i)
{
*tmp = value;
tmp++;
}*/
resize(n, value);
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
first++;
}
}
//拷贝构造
vector(const vector<T>& v)
{
size_t sz = v.size();
size_t cap = v.capacity();
T* tmp = new T[cap];
_start = tmp;
_finish = tmp + sz;
_endOfStorage = tmp + cap;
for (size_t i = 0; i < sz; ++i)
{
*tmp = v[i];
++tmp;
}
}
//赋值
vector<T>& operator=(vector<T> v)
{
//size_t cap = v.capacity();
//size_t sz = v.size();
//T* tmp = new T[cap];
//if (v.begin())
// memcpy(tmp, v._start, sizeof(T) * sz);
//_start = tmp;
//_finish = tmp + sz;
//_endOfStorage = tmp + cap;
swap(v);
return *this;
}
//析构
~vector()
{
if (_start)
{
delete[] _start;
_start = _endOfStorage = _finish = nullptr;
}
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endOfStorage - _start;
}
//扩容函数
void reserve(size_t n)
{
if (n > capacity())
{
//防止迭代器失效,先保存原大小
size_t Size = size();
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * Size);
delete[] _start;
}
_start = tmp;
_finish = tmp + Size;
_endOfStorage = tmp + n;
}
}
//重定义大小
void resize(size_t n, const T& value = T())
{
reserve(n);
while (n > size())
{
*_finish = value;
_finish++;
}
_finish = _start + n;
}
///access///
T& operator[](size_t pos)
{
return *(_start + pos);
}
const T& operator[](size_t pos) const
{
return *(_start + pos);
}
///modify/
void push_back(const T& x)
{
if (_finish == _endOfStorage)
{
size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
reserve(new_cap);
}
*_finish = x;
_finish++;
}
void pop_back()
{
assert(_start);
if (size())
{
_finish--;
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
}
iterator insert(iterator pos, const T& x)
{
assert(pos >= begin() && pos <= end());
if (_finish == _endOfStorage)
{
//防止迭代器失效
size_t len = pos - begin();
size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
reserve(new_cap);
pos = begin() + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
_finish++;
*pos = x;
return pos + 1;
}
iterator erase(iterator pos)
{
assert(pos >= begin() && pos < end());
size_t beg = pos - begin();
size_t sz = size();
while (beg < sz)
{
_start[beg] = _start[beg + 1];
beg++;
}
_finish--;
return pos;
}
void Test1()
{
push_back(1);
push_back(2);
push_back(3);
push_back(4);
push_back(5);
push_back(5);
push_back(5);
push_back(5);
pop_back();
pop_back();
pop_back();
}
void Test2()
{
reserve(20);
std::cout << _endOfStorage - _start << std::endl;
resize(10, 25);
std::cout << _finish - _start << std::endl;
}
void Test3()
{
vector<T> a(5, 20);
swap(a);
*this = a;
vector<T> copy(a);
}
void Test4()
{
insert(begin(), 1);
insert(begin(), 1);
insert(begin(), 1);
insert(begin(), 1);
erase(begin());
erase(begin());
erase(begin());
erase(begin());
erase(begin());
erase(begin());
}
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _endOfStorage; // 指向存储容量的尾
};
}