vector.h
#pragma once
#include <iostream>
namespace TCH
{
template<class T>
class vector
{
typedef T* iterator;
typedef const T* const_iterator;
public:
iterator begin()
{
return _start;
}
iterator end()
{
return _finsh;
}
const_iterator begin()
{
return _start;
}
const_iterator end()
{
return _finish;
}
vector()
:_start(nullptr)
,_finish(nullptr)
,_endof_storage(nullptr)
{}
template <class InputIterator>//----为现代写法做铺垫
vector(InputIterator first, InputIterator last)
: _start(nullptr)
, _finish(nullptr)
, _endof_storage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endof_storage, v._endof_storage);
}
vector(vector<T>& v)//------现代写法(拷贝构造肯定要用到“引用”)
: _start(nullptr)
, _finish(nullptr)//-----都要先对指针进行初始化
, _endof_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
vector(size_t n, const <T>& val)
: _start(nullptr)
, _finish(nullptr)//-----都要先对指针进行初始化
, _endof_storage(nullptr)
{
reserve(n);
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
vector& operator= (vector<T> v)//------赋值的现代写法
{
swap(v);
return *this;
}
size_t capacity()const//----尽量将const加上
{
return _endof_storage - _start;
}
size_t size()const
{
return _finsh - _start;
}
iterator insert(iterator pos, const T& val)
//void insert(iterator pos, const T& val)//------有迭代器失效的问题
{
//检查参数
assert(pos >= _start && pos <= _finish);
//扩容
if (_finish == _endof_storage)
{
size_t distance = pos - _start;//----解决pos出现野指针问题,先算好相对距离
size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
reverse(new_capacity);
pos = _start + distance;//---重新更新怕pos的位置
}
//挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = val;
_finish++;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finsih);
iterator begin = pos;
while (begin < _finsh)
{
*begin = *(begin + 1);
begin++;
}
_finish--;
return pos;
}
void resize(size_t n, T val = T())//--------使用匿名对象,调用默认构造函数,在C++内置类型是会有构造函数,
//若为int*则为nullptr等等其他类型
//这样才能更好的支持模板
{
if (n > capacity())
reverse(n);
if (n > size())
{
while (_finsh != _endof_storage)
{
*_finsh = val;
_finish++;
}
}
else
{
_finish = _start + n;
}
}
void reverse(size_t n)
{
size_t size = size();//先保存一份
size_t capacity = capacity();
if (n > capacity())
{
T* tmp = new T[n];
if(_start)//先判断有数据我们才拷贝
{
// memcpy(tmp, _start);
for (int i = 0; i < size(); i++)//---------实现更深层次的拷贝,解决浅拷贝的问题
{
tmp[i] = _start[i];
}
}
delete[] _start;
_start = tmp;
}
//_finsh = _start + size();//------要提前算一个size,因为start的地址改变了!
//_endof_storage = _start + capacity();//同上面一样的问题
_finish = _start + size;
_endof_storage = _start + capacity;
}
void push_back(const T& val)//--------引用传参重要性,若为string类型则要拷贝构造导致效率低下
{
/*if (_finish == _endof_storage)
{
size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
reverse(new_capacity);b
}
*_finish = val;//-------将值插入;
_finsh++;*/
insert(_finish, val);//-----复用
}
void pop_back()
{
if (_finish > _start)
_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];
}
~vector()
{
if (_start)
{
delete[] _start;
_finish = _endof_storage = _start = nullptr;
}
}
private:
iterator _start;
iterator _finish;
iterator _endof_storage;
};
}
1
void reverse(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
if(_start)//先判断有数据我们才拷贝
{
memcpy(_tmp, _start);
}
delete[] _start;
_start = tmp;
}
_finsh = _start + size();❌//------要提前算一个size,因为start的地址改变了!
_endof_storage = _start + capacity();❌
}
2
void resize(size_t n, T val = T())//--------使用匿名对象,调用默认构造函数,在C++内置类型是会有构造函数,
//若为int*则为nullptr等等其他类型
//这样才能更好的支持模板
3.insert()迭代器失效
第一个失效
✳️分析:
因为扩容后导致原来的pos指针发生野指针的错误
✳️解决办法:
可以先在扩容之前算相对位置
第二个失效
✳️分析:
因为我们只在函数里面修改了pos的位置,并没有影响外面的迭代器,导致也是出现野指针的问题
✳️解决办法:
这就要用到insert()函数的返回值了,并且此返回值应该是指向原来本应该指向的值
第三个失效
✳️分析:
就是返回迭代器的问题,只不过是迭代器意义改变了,应该是指向原来的值
1.
iterator insert(iterator pos, const T& val)--返回新插入元素的位置就是pos位置
//void insert(iterator pos, const T& val)
{
//检查参数
assert(pos >= _start && pos <= _finish);
//扩容
if (_finish == _endof_storage)
{
size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
reverse(new_capacity);
}
//挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = val;
_finish++;
return pos;------✳️返回迭代器!
}
2.
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = v.begin();
while(it != v.end())
{
if(*it % 2 ==0)
{
v.insert(it, 20);-------❌:此处会发生迭代器失效,因为insert后会发生扩容,扩容后it的空间失效了!
所以要解决 it 失效的问题
}
if++;
}
虽然我在insert里面解决了pos的问题,但是其为值传递并不能改变it的问题
除非用:“引用“来解决,但是”引用“在某些场景也会用不了
3。
vector<int>::iterator it = v.begin();
while(it != v.end())
{
if(*it % 2 == 0)
{
v.insert(it, 20);❌:这里也会有迭代器问题,此时会发生
it的意义改变,会导致重复一直在插入20!
}
if++;
}
✳️:解决2与3迭代器失效办法,则要用到insert()函数的返回值了,
应该返回新插入元素的位置;(上面insert函数改过来之后的那样的 )
4.对比VS下和Linux下不同平台对迭代器失效的处理解决方案![请添加图片描述](https://img-blog.csdnimg.cn/745fdfc8d93b476893e8e02cea6012d1.png)
5.erase()函数的讲解
对vector删除的理解![请添加图片描述](https://img-blog.csdnimg.cn/6186d2c10809456dbecd36e53e9d8333.png)
erase()迭代器失效理解![请添加图片描述](https://img-blog.csdnimg.cn/30497e29e9914daf903bb704974e4174.png)
erase()删除所有偶数引发迭代器失效
在VS下erase后返回的迭代器不能再用了 ,用了VS会直接报错!Linux可以
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
auto it = v.begin();
while(it != v.end())
{
if(*it % 2 ==0)
{
v.erase(it);
}
it++;------❌:只插入4个的话会有段错误,是因为删到第四个的时候,erase()
函数返回的迭代器是指向_finish,则再++it后永远不会等于_finish,所以会发生野指针的问题;
若插入5个的话便不会引发问题,所以代码程序逻辑有问题!
解决办法:若为偶数,删除之后就不执行it++逻辑
}
while(it != v.end())
{
if(*it % 2 ==0)
{
v.erase(it);
}
else-----✅:这样就对了
{
it++;
}
}
vector()构造的现代写法
string天然有参数来为现代写法来提供,而我vector的参数都是私有,但是可以由迭代器构造来为现代写法做铺垫
template <class InputIterator>//----为现代写法做铺垫
vector(InputIterator first, InputIterator last)
: _start(nullptr)
, _finish(nullptr)
, _endof_storage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endof_storage, v._endof_storage);
}
vector(vector<T>& v)//------现代写法(拷贝构造肯定要用到“引用”)
: _start(nullptr)
, _finish(nullptr)
, _endof_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
vector& operator= (vector<T> v)//------赋值的现代写法
{
swap(v);
return *this;
}
调用匹配出错问题
vector<int> (10, 2);
按理来说会调用
vector(size_t n, const T& val);
这个构造函数,但是编译器会去调用此构造函数
template <class InputIterator>//----为现代写法做铺垫
vector(InputIterator first, InputIterator last);
下面图片会给出解释
则需要我们重新写个重载函数
vector(int n, const T& val);---这样就不会出现匹配错误的出现
(不会解决的时候,可以去看看源代码是怎么去解决的)
vector的深浅拷贝问题
正常的扩容是没有问题的,但是vector<vector>就会有问题, 其扩容的时候会调用拷贝构造,拷贝构造又会去调用我们写的迭代器区间构造函数去构造,把tmp构造时会调用push_back,我前期插入4个vector没有问题,插入第五个vector的时候我需要扩容;我们原来vector存储int的时候扩容用的memcpy没问题,而现在的vectotr存储的数据用memcpy则有问题,memcpy是把位置空间依次拷贝下去,现在的空间位置不是int了,现在是vector,好了你拷贝下去了,拷贝下去的几个vector还是指向被拷贝的vector,则当我们delete的时候,我们顺便也就吧拷贝的空间也给delete了,因为指向同一块空间嘛,所以第五组数据没问题,前面几组的数据都是随机值
解决办法:
那我们一个一个拷贝,若你是内置类型我就把值赋值给他,若你是自定义类型,那么我吧这个string赋值给另一个string,则会调用自己的拷贝构造;如果我是vector,那我么我吧这个vector赋值给你那个vector,则它会调用vector的拷贝构造 (因为string,vector拷贝构造本身就会实现深拷贝)