> 文章列表 > 【C++】vector的实现

【C++】vector的实现

【C++】vector的实现

模拟实现vector类

  • 前言
  • 一、迭代
  • 二、重载 [ ]
  • 三、构造函数相关(重点)
    • (1)构造函数
    • (2)构造并使用n个值为value的元素初始化
    • (3)区间构造
    • (4)拷贝构造
  • 三、析构函数
  • 四、[赋值运算符重载](https://so.csdn.net/so/search?q=%E8%B5%8B%E5%80%BC%E8%BF%90%E7%AE%97%E7%AC%A6%E9%87%8D%E8%BD%BD&spm=1001.2101.3001.7020)
  • 五、reserve的实现--改变容器容量大小
  • 五、resize的实现--改变容器有效字符个数
  • 六、几种简单接口(empty、size、capacity、clear)
  • 七、push_back和pop_back的实现
  • 八、insert的实现(重点)--迭代器失效问题一
  • 八、erase的实现(重点)--迭代器失效问题二

前言

类框架与之前string类的模拟实现类似,区别在于vector 是用 _finish 和 _end_of_storage 两个指针来维护空间,但本质上其实是一样的 ,
_size = _finish - _start
_capacity = _end_of_storage - _start

class vector
{public://成员函数private:iterator _start;iterator _finish;iterator _endofstorage;
}

接下来介绍成员函数的实现。

一、迭代器

iterator 与 const_iterator 的作用:
遍历容器内的元素,并访问这些元素的值。

iterator 与 const_iterator 的区别:
iterator 可以改元素值,但 const_iterator 不可以改元素值。

const_iterator 对象可以用于 const 对象 或非 const 对象,它自身的值可以改(可以指向其他元素),但不能改写其指向的元素值。

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;}

二、重载 [ ]

T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}

三、构造函数相关(重点)

(1)构造函数

直接将所有的成员变量置为空即可

vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}

(2)构造并使用n个值为value的元素初始化

vector(int n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);for (int i = 0; i < n; ++i){push_back(val);}}

其中,第二个参数val是一个缺省参数。对于没有给定确定值的时候,有两种情况:
(1)如果T是内置类型,T()的值为0;
(2)如果T是自定义类型,T()调用的就是该自定义类型的默认构造函数。
因此,对于自定义类型一定要有默认构造函数,否则会报错!
重点在于第一个参数的类型,只能是int,如果是size_t类型,会报错。原因如下:
接口测试部分
构造函数错误调用问题

(3)区间构造

//迭代器区间构造template <class InputIterator>vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);++first;}}

为什么单独定义一个这样的函数模板呢?template <class InputIterator>
答:这是因为vector是一个底层连续的容器,它可以用来存储任意类型的数据,
而这里使用区间的方式来初始化vector的容器就需要做到对任意数据都可以进行初始化,因此就需要以模板的形式给出。

(4)拷贝构造

拷贝构造有三种实现方式,最推荐的当然是通过vector内部的swap()函数实现
参数v是const修饰的,它只能调用const类型的成员函数,因此这里必须调用const修饰的迭代器:

//拷贝构造 -- 现代写法vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){vector<T> tmp(v.begin(), v.end()); //复用构造函数和swap函数swap(tmp);}//写法2//vector(const vector<T>& v)//{//	T* tmp = new T[v.capacity()];//	memcpy(tmp, v._start, sizeof(T) * v.capacity());//	_start = tmp;//	_finish = _start + v.size();//	_end_of_storage = _start + v.capacity();//}//写法3//vector(const vector<T>& v)//	: _start(nullptr)//	, _finish(nullptr)//	, _end_of_storage(nullptr)//{//	reserve(v.capacity());//	for (size_t i = 0; i < v.size(); i++)//		push_back(v[i]);//}

三、析构函数

~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}

四、赋值运算符重载

vector<T>& operator=(vector<T> v){swap(v);return *this;}

五、reserve的实现–改变容器容量大小

【C++】vector的实现

void reserve(size_t n){if (n > capacity()){size_t oldSize = size();T* tmp = new T[n];if (_start){// 扩容必须深拷贝,否则会导致迭代器失效for (size_t i = 0; i < oldSize; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = tmp + oldSize;_endofstorage = _start + n;}}

五、resize的实现–改变容器有效字符个数

【C++】vector的实现

void resize(size_t n, T val = T()){if (n > capacity()){reserve(n);}if (n > size()){while (_finish < _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}}

六、几种简单接口(empty、size、capacity、clear)

bool empty() const{return _finish == _start;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}
void clear(){_finish = _start;}

七、push_back和pop_back的实现

void push_back(const T& x){if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish = x;++_finish;}void pop_back(){assert(!empty());--_finish;}

八、insert的实现(重点)–迭代器失效问题一

//迭代器失效:扩容引起的野指针问题iterator insert(iterator pos, const T& val){assert(pos >= _start);assert(pos < _finish);if (_finish == _endofstorage){size_t len = pos - _start; //记录pos,避免扩容后pos变为野指针size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);pos = _start + len; //更新pos}//挪动数据iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}

迭代器失效问题一:这里是由于insert之后pos 的意义变了–不再指向原来的元素,而是指向新插入的元素。因此,如果不读pos进行更新,pos就会成为野指针。

八、erase的实现(重点)–迭代器失效问题二

iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *(begin);++begin;}--_finish;return pos;}

迭代器失效问题二:这里是由于erase之后pos 的意义变了,分为两种情况:
(1)删除元素后pos 不再指向原来的元素,而是指向该元素的后一个元素,所以 erase 之后会导致一个元素被跳过;
(2)在极端情况下–删除最后一个元素之后,pos 就等于 _finish,会发生越界。

因此insert 和 erase 之后迭代器失效,必须更新后才能再次使用。

具体的更新措施:
【C++】vector的实现

【C++】vector的实现