成员变量
vector<>中可能是任意的数据类型,可能是譬如int,double这种内置类型,也有可能是类似于string类型的这种自定义类型,所以这里就需要使用到模版以及定义类型的指针
template<class T>
typedef T* iterator;
成员变量一共有三 start,finish,endofstorage,切都是使用的iterator指针类型的 ,这样我们可以直接找到该地址,而不是用下标去访问,我们可以直接在声明的时候初始化,也可以在构造函数的时候使用初始化列表初始化
iterator _start=nullptr;
iterator _finish=nullptr;
iterator _endofstorgae=nullptr;
构造,拷贝,析构函数
构造函数
构造函数由于本质是指针,因为我们已经在刚开始的时候在成员变量的时候初始化过了,所以可以不需要写初始化列表
vector()
:_start(nullptr),
_finish(nullptr),
_endofstorgae(nullptr)
{}
拷贝构造函数
假设我们要将v1的拷贝构造给v2,那么我们需要先给v2reserve出和v1相同的capacity,这个时候我们去取v1的capacity,然后我们再将v1的值pushback给v2
vector(const vector<T>& v)
// :_start(nullptr),
// _finish(nullptr),
// _endofstorgae(nullptr)
{
reserve(v.capacity());
for(auto& e:v){
push_back(e);
}
}
析构函数
析构函数我们直接delete start然后让我让start,finish和endofstorage指向空
~vector(){
delete[] _start;
_start=_finish=_endofstorgae=nullptr;
}
操作符
operator=
这边我们可以直接写一个swap的函数去调用库中的swap函数
标准库中是将两个地址交换一下
所以这边的swap函数就是将三个成员函数的地址交换一下,而原来this指针是指向空的,将空地址和tmp的地址使用swap函数去交换一下,值的注意的是,这边的swap里面的变量是用的赋值传参,而不是使用的引用传参,如果使用引用传参就会取到=右边的地址了,系统就会报错
void swap( vector<T> v){
std::swap(_start,v._start);
std::swap(_finish,v._finish);
std::swap(_endofstorgae,v._endofstorgae);
}
vector<T>& operator=(const vector<T>& tmp){
swap(tmp);
return *this;
}
operator[]
取pos位置前需要先判断一下pos的位置是否合法
T& operator[](size_t pos){
assert(pos<size());
return _start[pos];
};
const T& operator[](size_t pos)const{
assert(pos<size());
return _start[pos];
};
增删查改
capacity和size
endofstorage-start就是整个vector的capacity,如果只是定义未初始化,那么初始化的时候都给他们指向空了,所以等于0,size也是同样的道理
size_t capacity()const{
return _endofstorgae-_start;
}
size_t size()const{
return _finish-_start;
}
迭代器
迭代器我们设置一个只读的迭代器和可写迭代器begin指向start,end指向finish
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;
}
reserve
在一开始的时候我们先用一个变量记录一下size的值,因为后面等start的地址发生改变再想取size的值就会出错的情况。如果n大于capacity再扩容,先定义一个tmp变量,如果start不为空则拷贝,这里需要值得注意,不能够使用memcpy去拷贝,这里涉及到一个深层次浅拷贝到问题,sizeof(T)为T成员变量的使用空间,如果这里是一个int类型的完全ok,如果是一个字符串类型的,这个_start会指向一块堆上的空间,这个空间有多大根据使用情况来定的,如果只用sizeof(T)那么则可能会发生报错的风险。
而使用for循环去拷贝,那么就不会造成报错,拷贝完后则del原来的start,再重新拷贝成员变量
void reserve(size_t n){
size_t sz=size();
if(n>capacity()){
T* tmp=new T[n];
if(_start){
// memcpy(tmp,_start,sizeof(T)*size());//涉及到生层次浅拷贝问题
for(size_t i=0;i<sz;i++){
tmp[i]=_start[i];
}
delete[] _start;
}
_start=tmp;
_finish=sz+_start;
_endofstorgae=_start+n;
}
}
resize
resize就是控制size大小的,如果n<=size的话,那么直接让finish变成start+n即可。如果大于则扩容,扩容完则从finish位置去填入val,这里需要注意的是val不能填0,这边在定义的时候可能是其他的数据类型
void resize(size_t n,const T& val=T()){
if(n<=size()){
_finish=_start+n;
}else{
reserve(n);
while(_finish!=_start+n){
*_finish=val;
_finish++;
}
}
}
string类型的resize
int*类型的resize
push_back
先判断是否需要扩容,如果需要扩容则扩容,扩完容则赋值
void push_back(const T& x){
if(_finish==_endofstorgae){
reserve(capacity()==0?4:2*capacity());
}
*_finish=x;
_finish++;
}
insert
先判断pos是否是合法位置,是的话再插入
void insert(iterator pos,const T& x){
assert(pos>=_start);
assert(pos<=_finish);
if(_finish==_endofstorgae){
size_t len=pos-_start;
reserve(capacity()==0?4:2*capacity());
pos=_start+len;
}
iterator end=_finish-1;
while(pos<=end){
*(end+1)=*end;
end--;
}
*pos=x;
_finish++;
}
扩容引发迭代器失效问题1
扩容有可能会导致迭代器失效,如果上面这段代码的insert成员函数改成下面这样,扩容后pos还在原来的地址上,会导致迭代器失效的问题,此时会可能会发生内存错误
void insert(iterator pos,const T& x){
assert(pos>=_start);
assert(pos<=_finish);
if(_finish==_endofstorgae){
// size_t len=pos-_start;
reserve(capacity()==0?4:2*capacity());
//pos=_start+len;
}
iterator end=_finish-1;
while(pos<=end){
*(end+1)=*end;
end--;
}
*pos=x;
_finish++;
}
扩容引发迭代器失效问题2
我们先定义一个it指针指向v.begin(),那么我们插入到扩容到时候,此时可能会引发异地扩容,那么it还是指向了之前的首地址,所以也就发生了失效问题
erase
erase代表了删除某个位置的值,我们先判断一下pos位置是否合法,再取it下一个位置的地址,将下一个位置的值往前移动则覆盖完成。
iterator erase(iterator pos){
assert(pos>=_start);
assert(pos<_finish);
iterator it=pos+1;
while(it!=_finish){
*(it-1)=*it;
it++;
;
}
_finish--;
return pos;
}
删除引发的迭代器失效问题
下面这段代码有可能会引发迭代器失效,不同的编译器下检查的方式不一样,如果i%2成立删除了元素,那么会造成迭代器向后移动一个,再it++,就会跳过需要判断的数值
使用其他数据类型来进行初始化
可以使用其他类型的数据结构来进行初始化,比如string使用int类型的迭代器区间,可能打进去的是字母
template<class InputIterator>
vector(InputIterator first,InputIterator last)
:_start(nullptr),//clion编译器未做处理,在vs下会处理
_finish(nullptr),
_endofstorgae(nullptr)
{
while(first!=last){
push_back((*first));
++first;
}
}
在初始化的时候赋值
这里的T& val=T()是代表了取的是T数据类型的空值,int的话可能是0,string可能是空
vector(size_t n,const T& val=T()){
reserve(n);
for(size_t i=0;i<n;i++){
push_back(val);
}
}
vector(int n,const T& val=T()){
reserve(n);
for(int i=0;i<n;i++){
push_back(val);
}
}