目录
构造函数
无参构造
用n个val来初始化的拷贝构造
拷贝构造
用迭代器初始化
析构函数
reserve
resize
pushback
pop_back
迭代器及解引用
迭代器的实现
解引用[ ]
insert
erase
赋值拷贝
补充
vector底层也是顺序表,但是vector可以储存不同的类型包括自定义类型和内置类型,所以在实现vector的时候要用模板实现。vector的成员变量与string是不同的,vector的成员变量是3个迭代器,分别是指向顺序表起始位置的指针,指向最后一个数据下一个位置的指针以及指向尾部开辟的空间。
namespace cfl
{template<class T>class vector{typedef T* iterator;public:private:iterator _start; //指向顺序表的开头iterator _finish; //指向顺序表最后一个有效数据的下一个位置iterator _end_of_storage; //指向最后一个开辟的空间的下一个位置};
}
构造函数
vector的构造函数有5个,我们先实现4个,有一个涉及到容器分配器在写容器篇博客时会实现。
无参构造
//无参构造
vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr)
{}
用n个val来初始化的拷贝构造
//用n个val来初始化的拷贝构造
vector(size_t n, const T& val)
{_start = new T[n];for (int i = 0; i < n; i++)_start[i] = val;_finish = _start + n;_end_of_storage = _start + n;
}
拷贝构造
注意在进行拷贝构造的时候,不建议使用memcpy,因为在使用memcpy的时候内置类型不会出问题,但是对于自定义类型可能会出现浅拷贝,自定义类型中有指针就只是简单的拷贝其地址,导致两个vector指向同一块位置,就会调用两次析构函数。
size_t size()const
{return _finish - _start;
}size_t capacity()const
{return _end_of_storage - _start;
}//拷贝构造
vector(const vector& v)
{_start = new T[v.capacity()];//memcpy(_start, v._start, v.size()*sizeof(T));用memcpy,如果T是自定义类型可能会导致浅拷贝比如T是string,则指挥拷贝string的地址,导致两个vector指向一个位置//用for循环和赋值运算符重载来实现for (int i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();_end_of_storage = _start + v.capacity();
}
用迭代器初始化
此处使用迭代器初始化为了方便直接调用push_back(在后面实现)。
//用迭代器初始化
template<class InputIterator> //此处用一个函数迭代器,使得vector可以用各种类型初始化,能用int数组也能用string等
vector(InputIterator first, InputIterator last)
{//直接调用push_back让其去开辟空间while (first != last){push_back(*first);++first;}
}
析构函数
~vector()
{delete[] _start;_start = _finish = _end_of_storage = nullptr;
}
reserve
用reserve进行空间的开辟,与拷贝构造同理,这里也不建议使用memcpy进行拷贝。
void reserve(size_t n)
{if (n > capacity()){iterator tmp = new T[n];int sz = size();for (int i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}
resize
resize传参时如果小于size(),就会对空间进行缩小,如果大于就会额外对额外的空间进行初始化。
void resize(size_t n,const T& val=T()) //如果没有传val就用编译器默认的初始化
{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _end_of_storage){*(_finish) = val;++_finish;}}
}
pushback
void push_back(const T& val)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*(_finish) = val;++_finish;
}
pop_back
尾删。
void pop_back()
{_finish--;
}
迭代器及解引用
迭代器的实现
对于迭代器的实现就是实现begin和end。
迭代器的实现也分为两种:非const版本和const版本。
iterator begin()
{return _start;
}
iterator end()
{return _finish;
}typedef const T* const_iterator;
const_iterator begin()const
{return _start;
}
const_iterator end()const
{return _finish;
}
解引用[ ]
//解引用运算符重载
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}
insert
insert插入。insert传的pos是在_start和_finish之间的,所以在扩容后对导致_start指向新的空间了,但是pos位置还指向原位置,所以扩容后要对pos位置进行更新。
void insert(iterator pos, const T& val)
{assert(pos < _finish);if (_finish == _end_of_storage){int num = pos - _start; //如果想开辟空间,会导致_start指向新的位置但是pos还指向原处reserve(2 * capacity());pos = _start + num; //对pos位置进行更新}iterator end = _finish;while (end > pos){*(end) = *(end - 1);--end;}*pos = val;++_finish;
}
insert后不能再使用pos这个迭代器了,因为他可能失效。 可以用一个返回值来返回新的pos位置。
erase
指定位置删除。
void erase(iterator pos)
{iterator cur = pos;while (cur + 1 < _finish){*(cur) = *(cur + 1);++cur;}--_finish;
}
赋值拷贝
void swap(T& tmp)
{swap(this->_start, tmp._start);swap(this->_finish, tmp._finish);swap(this->_end_of_storage, tmp._end_of_storage);
}//赋值拷贝
T& operator=(T tmp)
{swap(tmp);
}
此处赋值拷贝还是使用交换临时变量的方法,即能够将tmp拷贝构造的空间给this还能让tmp去销毁this的原本数据。
补充
一些vector不常用的成员函数。
1)rbegin,rend从后往前开始;
2)clear()清除vector中的数据,不清除空间;
3)vector<vector<T>>实现用vector模拟二维数组。