在实现本文的vector模拟前,建议先了解关于vector的必要知识:【C++】容器vector常用接口详解-CSDN博客https://blog.csdn.net/2301_80555259/article/details/141529230?spm=1001.2014.3001.5501
目录
一.基本结构
二.构造函数(constructor)
三.迭代器(iterator)
四.容量操作(capacity)
1.resize
2.reserve
五. 修改操作(modify)
六.访问操作(access)
七.重载赋值运算符
八.析构函数(destructor)
九.打印(print)
一.基本结构
创建头文件:vector.h 实现文件:vector.cpp 测试文件:test.cpp
当然此处为了简化直接使用了头文件+测试文件,在自定义的命名空间内创建vector类,配上基本结构:
由于vector是顺序表,因此和C语言实现顺序表时一样,至上有三个参数:
- 指向一段连续空间的指针
- 空间的有效大小
- 空间的实际大小
实现vector的迭代器,可以就将其视为指针,因此可以使用三个迭代器(指针)就完成上述三个参数的实现,它们分别是_start,_finish,_end_of_storage,此时
- 指向一段连续空间的指针:_start
- 空间的有效大小size:_finish - _start
- 空间的实际大小capacity:_end_of_storage - _start
template<class T>
class vector
{
public://用指针实现vector的迭代器typedef T* iterator;typedef const T* const_iterator;
private:iterator _start;iterator _finish;iterator _end_of_storage;
};
二.构造函数(constructor)
//无参构造
vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr)
{}
//带参构造,用n个value来初始化vector
vector(int n, const T& value = T()):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{reserve(n);for (int i = 0; i < n; i++){push_back(value);}
}
//拷贝构造
vector(const vector& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr)
{//易错点:这里并没有进行初始化reserve(v.size());for (auto e : v){push_back(e);}
}//迭代器区间构造
template<class InputIterator>
vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{while (first != last){push_back(*first);first++;}
}
这里单独把这个构造拿出来谈谈, 最开始我只在无参构造中使用初始化列表,没有对其他构造函数的_start等进行初始化,这个bug我找了半天,每一个构造也是构造函数,跟其他构造函数没有关系,是凭借拷贝构造自身创建一个新的对象,因此也要进行初始化序列来初始化。这是最为保险的,否则就要在成员变量声明处加上默认值。
发现这个错误的bug就在使用赋值重载时,临时变量需要调用拷贝构造,但若此时拷贝构造没有去手动写初始化列表,那么其_finish和_end_of_storage不是nullptr,是随机值,这样就导致了错误然而在有参构造时,尽管没去写初始化列表,对应的_start和_finish等也都是nullptr,此时就没有问题,但是为了保险,我建议所有构造函数都应该去手写一遍初始化列表。
三.迭代器(iterator)
iterator begin()
{return _start;
}
iterator end()
{return _finish;
}
const_iterator begin()const
{return _start;
}
const_iterator end()const
{return _finish;
}
四.容量操作(capacity)
容量操作方面的函数有:
- size
- capacity
- empty
- resize
- reserve
前三个较为简单,很容易实现:
size_t size()const
{return _finish - _start;
}
size_t capacity()const
{return _end_of_storage - _start;
}
bool empty()const
{return size() == 0;
}
1.resize
resize会改变size的大小,同时也有可能改变capacity的大小,具体要分三种情况:
- n大于capacity时:直接套用reserve扩容后再初始化新内容
- n大于size小于capacity时:有效值不变的情况下初始化新内容
- n小于size时:直接将size缩小到n
void resize(size_t n, const T& value = T())
{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = value;_finish++;}}
}
如何来理解:const T& value = T() ?
在C++中内置类型(如int,double,char)特殊处理过,升级为了类,也有默认构造函数,它会将变量初始化为默认值。
此处使用了匿名对象int tmp1 = int();//0 int tmp2 = int(10);//10
2.reserve
reserve扩容也是同理,只在n大于capacity的时候进行扩容
void reserve(size_t n)
{if (n > capacity()){//记录原本的数据个数size_t old_size = size();iterator tmp = new T[n];memcpy(tmp, _start, old_size * sizeof(T));delete[] _start;_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}
}
五. 修改操作(modify)
//modify
void push_back(const T& x)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;
}
void pop_back()
{assert(size() > 0);--_finish;
}
iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){//注意这里扩容会使原有的pos迭代器失效//因此这里使用len计算相对长度,更新迭代器size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish;while (end > pos){*end = *(end - 1);end--;}*pos = x;++_finish;return pos;
}
void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;it++;}--_finish;
}
六.访问操作(access)
//access
T& operator[](size_t i)
{assert(i < size());return _start[i];
}
const T& operator[](size_t i)const
{assert(i < size());return _start[i];
}
七.重载赋值运算符
注意这里的参数不是常量引用,而是按值传递。这是因为之后调用swap函数,按值传递可以使得传入的参数会被复制一份给临时对象,从而避免了对原对象的修改。
//operator =
void swap(vector& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}//在模板内,写vector和vector<T>都行
//因为要交换内部内容,因此不加const
vector& operator=(vector v)
{swap(v);return *this;
}
八.析构函数(destructor)
~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}
九.打印(print)
template<class T>
void print_vector(const vector<T>& v)
{//规定,从没有实例化的类模版中取东西//编译器不能区分const_iterator是类型还是静态成员变量,因此要加typename//当然也可以更简单写为auto it = v.begin();typename vector<T>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}template<class container>
void print_container(const container& v)
{for (auto e : v){cout << e << " ";}cout << endl;
}
本文结束,下次将会介绍容器中的list,和vector是类似的,但也有不同,欢迎持续关注