前言
在本文中,我们的目标是使用C++以及模板的知识来对vector进行模拟实现以及测试。
一、基本框架
为了避免命名冲突,我们vector的实现是在命名空间myvector中的,我们创建两个文件,vector.h以及test.cpp,前者来实现vector,后者来对我们实现的vector进行测试。
通过查看vector的底层源代码,我们可以得知:
1.vector中有三个成员:_start,_finish,_end_of_storge;
2._start代表vector开始的地址,_finish代表数据存储结束的后一个位置,_end_of_storge代表vector后的一个位置
二、功能预览
在模拟实现vector中,我们需要实现以下功能:
namespace myvector {
template<typename T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
private:
T* _start;
T* _finish;
T* _end_of_storge;
public:
//构造函数与析构函数
vector();
vector(size_t n, const T& value = T());
vector(int n, const T& value = T());
template<class InputIterator>
vector(InputIterator first, InputIterator last);
vector(const vector<T>& v);
vector<T>& operator=(vector<T> v);
vector<T>& operator=(initializer_list<T> l);
~vector()
//迭代器
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
//容量相关
size_t size()const;
size_t capacity()const;
void reserve(size_t n);
void resize(size_t n, const T& value = T());
T& operator[](size_t pos);
const T& operator[](size_t pos)const;
//修改操作
void push_back(const T& x);
void pop_back();
void swap(vector<T>& x);
iterator insert(iterator pos, const T& x);
iterator erase(iterator pos);
};
}
三、构造函数与析构函数
1.构造函数
我们提供了四种构造函数
1) 无参构造
vector()
: _start(nullptr)
, _finish(nullptr)
, _end_of_storge(nullptr){
}
作用:不传参的默认构造函数
实现:将_start,_finiish,_end_of_storge指向空指针,表示此时vector为空
2)使用n个T类型对象进行构造
vector(size_t n, const T& value = T())
: _start(nullptr)
, _finish(nullptr)
, _end_of_storge(nullptr){
reserve(n);
while (n--){
push_back(value);
}
}
vector(int n, const T& value = T())
: _start(new T[n])
, _finish(_start + n)
, _end_of_storge(_finish){
for (int i = 0; i < n; ++i){
_start[i] = value;
}
}
注意:这里分别用size_t和int类型的n实现一次的原因是对于 vector<int> v(10, 5);这样的构造参数,编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型就不会走vector(size_t n, const T& value = T())这个构造方法, 最终选择的是:vector(InputIterator first, InputIterator last),因为编译器认为区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int,但是10和5根本不是一个区间,编译时就报错了故需要增加该构造方法。
3)迭代器构造
template<class InputIterator>
vector(InputIterator first, InputIterator last){
while (first != last){
push_back(*first);
++first;
}
}
说明:传入两个迭代器,将二者之间的元素 填入vector中
4)拷贝构造函数
vector(const vector<T>& v)
: _start(nullptr)
, _finish(nullptr)
, _end_of_storge(nullptr){
reserve(v.capacity());
iterator it = begin();
const_iterator vit = v.begin();
while (vit != v.end()){
*it++ = *vit++;
}
_finish = it;
}
5)赋值重载函数
vector<T>& operator=(vector<T> v){
swap(v);
return *this;
}
注意:这里的swap并不会和传入的vector交换,因为传入的参数v是传值调用,v是传入的vector对象的一份临时拷贝。所以并不会改变原来的vector对象。
2.析构函数
~vector(){
if (_start){
delete[] _start;
_start = _finish = _end_of_storge = nullptr;
}
}
实现:释放_start指向的空间,将_start,_finish,_end_of_storge置空。
3.测试
我们通过以下代码来进行测试
预期结果:编译通过并且无程序崩溃。
//测试构造函数与析构函数
void test1() {
myvector::vector<int>v1;
myvector::vector<int>v2(5, 10);
myvector::vector<int>v3(v2);
myvector::vector<int>v4(v3.begin(), v3.end());
myvector::vector<int>v5 = v4;
}
运行结果:
通过!!!
四、迭代器函数
注意:在这里,迭代器分两种,一种是可修改的迭代器,一种是不可修改的迭代器,所以我们需要写两套迭代器函数。迭代器的命名不得随意修改,必须使用规定的命名,否则无法使用快捷for循环遍历。
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;
}
测试
我们通过以下代码对迭代器相关函数进行测试
预期结果:v1打印12个7,v2打印12个6
/测试迭代器函数
void test2(){
myvector::vector<int>v1(12,6);
const myvector::vector<int>v2 = v1;
myvector::vector<int>::iterator it1 = v1.begin();
while(it1 != v1.end()) {
(*it1)++;
cout << *it1 << " ";
it1++;
}
cout << endl;
myvector::vector<int>::const_iterator it2 = v2.begin();
while (it2 != v2.end()) {
cout << *it2 << " ";
it2++;
}
cout << endl;
}
通过!!!
五、大小容量相关函数
1)size()函数
作用:获取数据大小
实现:使用_finish直接与_start相减得到两个指针之间间隔的元素个数,即为元素个数
size_t size()const {
return _finish - _start;
}
2)capacity()函数
作用:获取vector当前容量大小
实现:使用_end_of_storge直接与_start相减得到两个指针之间间隔的距离,即为容量大小
size_t capacity()const {
return _end_of_storge - _start;
}
3)reserve()函数
作用:将vector容量扩容至n,若n<capacity(),则不进行操作
实现:先记录当前vector中元素的个数,以防后续无法确定_finish的位置,申请一块大小为n的新空间,将原来的数据拷贝到新的空间中,再将原来的_start指向新的空间,更新_finish和_end_of_stroge。
void reserve(size_t n) {
if (_end_of_storge - _start < n) {
size_t old_size = _finish - _start;
T* tmp = new T[n];
if (tmp) {
memcpy(tmp, _start, sizeof(T) * (_end_of_storge - _start));
delete[]_start;
_start = tmp;
}
_finish = _start + old_size;
_end_of_storge = _start + n;
}
}
4)resize()函数
作用:将vector的大小调整为n,如果原来的元素个数多于n,则将元素个数删至n,如果原来的元素个数小于n,则在原来的数据后面填充元素value,直至元素个数为n
实现:如果n<size(),直接将_finish调整为_start+n,否则从_finish+1开始,填充元素value,直到元素个数为n。
void resize(size_t n, const T& value = T()) {
if (n < _finish - _start) {
_finish = _start + n;
return;
}
if (n > _end_of_storge - _start) {
reserve(n);
}
iterator t = _finish;
_finish = _start + n;
while (t != _finish) {
*t = value;
++t;
}
}
5)[]重载
作用:返回第n个元素
T& operator[](size_t pos) {
assert(pos < size());
return _start + pos;
}
const T& operator[](size_t pos)const {
assert(pos < size());
return _start + pos;
}
注意:由于const修饰的对象也需要使用[]访问,并且使用const修饰的对象访问的元素不能被修改,所以我们需要两个不同的[]重载。
测试
我们通过以下的代码进行测试:
void test3() {
myvector::vector<int>v5(10, 2);
myvector::vector<string>v6(20, "helle world!");
cout << "v5:capacity:" << v5.capacity()<<"size():"<<v5.size() << endl;
cout << "v6:capacity:" << v6.capacity()<<"size():"<<v6.size() << endl;
v5.reserve(10);
cout << "v5:capacity:" << v5.capacity() << "size():" << v5.size() << endl;
v6.resize(12,"hehe");
cout << "v6:capacity:" << v6.capacity() << "size():" << v6.size() << endl;
for (int i = 0; i < v5.size();i++) {
cout << v5[i] << " ";
}
cout << endl;
}
预期结果:按照函数作用输出结果
通过!!!
六、操作函数
1)push_back()函数
作用:在vector的末尾插入一个元素
实现:先检查vector是否满了,如果满了需要进行扩容操作,然后将_finisgh位置赋值为x,_finish后移一位即可。
void push_back(const T& x) {
if (_finish == _end_of_storge) {
reserve(_end_of_storge - _start == 0 ? 4 : 2 * (_end_of_storge - _start));
}
*_finish = x;
++_finish;
}
2)pop_back()函数
作用:如果vector不为空,则将vector最后一个元素删除
实现:将_finish前移一位即可
void pop_back() {
if (_finish != _start) {
--_finish;
}
}
3)swap()函数
作用:交换两个vector的内容
实现:调用stl库中的swap将_start,_finish,_end_of_storge分别交换即可
void swap(vector<T>& x) {
std::swap(this->_start, x._start);
std::swap(this->_finish, x._finish);
std::swap(this->_end_of_storge, x._end_of_storge);
}
4)insert()函数
作用:在pos位置擦插入一个元素
实现:先检查vector需不需要扩容,再将pos后面的元素全部后移一个位置,将pos位置的值改为x,再将_finish往后移一个位置即可
iterator insert(iterator pos, const T& x) {
assert(pos <= _finish);
// 空间不够先进行增容
if (_finish == _end_of_storge)
{
//size_t size = size();
size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
reserve(newCapacity);
// 如果发生了增容,需要重置pos
pos = _start + size();
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
5)erase()函数
作用:删除pos位置的元素
实现:将pos后面的元素前移一个位置即可
iterator erase(iterator pos) {
// 挪动数据进行删除
iterator begin = pos + 1;
while (begin != _finish) {
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
测试
我们通过以下代码进行测试:
预期结果:按照函数作用输出结果
void test4() {
myvector::vector<int>v1(10, 2);
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
v1.push_back(4);
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
v1.pop_back();
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
v1.insert(v1.begin(),3);
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
v1.erase(v1.begin());
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
myvector::vector<int>v2(2, 7);
v1.swap(v2);
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
}
通过!!!