1、vector模拟实现
2、迭代器失效问题
3、深浅拷贝导致的程序崩溃问题分析
1、vector模拟实现
namespace vec1
{
template <class T>
class vector
{
public:
typedef T* iterator; //vector中的iterator迭代器可以是原生指针
typedef const T* const_iterator //用来访问const变量或函数的迭代器
iterator begin() //返回vector的头位置
{
return _start;
}
iterator end() //返回vector的尾位置
{
return _finish;
}
const_iterator begin() const //返回vector的头位置,且返回值无法修改
{
return _start;
}
const_iterator end() const //返回vector的尾位置,且返回值无法修改
{
return _finish;
}
vector() //默认构造函数
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{}
vector(const vector<T>& v) //拷贝构造函数
{
_start = new T[v.size()];
//memcpy(_start, v._start, sizeof(T)*v.size()); //(涉及到深浅拷贝问题)
for(size_t i = 0; i < v.size(); ++i)
{
_start[i] = v.start + v.size();
}
_finish = _start + v.size();
_end_of_storage = _start + v.size();
}
vector(size_t n, const T& val = T()) //给n个值的半缺省构造函数
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
reserve(n); //扩容
for(size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
template <class InputIterator>
vector(InputIterator first, InputIterator last) //利用两个迭代器来初始化,可以传
//任意数据类型的数据
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
while(first != last)
{
push_back(*first);
++first;
}
}
void swap(vector<T>& v) //用于两个vector容器的交换
{
std::swap(_start, v.start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
vector(const vector<T>& v) //拷贝构造函数,间接型的
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
vector<T> tmp(v.begin,v.end()); //利用两个迭代器将v的数据拷贝给tmp
swap<tmp>; //再将tmp与要拷贝给的对象互换
}
~vector() //析构函数
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
size_t capacity() const //返回容量大小
{
return _end_of_storage - _start;
}
const T& operator[](size_t pos) const //重定义[]访问运算符
{
assert(pos < size());
return _start[pos];
}
size_t size() const //返回容器内数据个数
{
return _finish - _start;
}
void reserve(size_t n)
{
if(n > capacity())
{
size_t sz = size(); //提前记录下数据个数,方便下面更新(涉及到迭代器失效)
T*tmp = new T[n];
if(_start)
{
//memcpy(tmp, _start, sizeof(T)*sz); //同上面所说过的涉及到深浅拷贝
for(size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
void resize(size_t n, const T& val = T()) //给容器空间重新赋予容量
{
if(n > capacity())
{
reserve(n);
}
if(n > size()) //如果新的值比原来的数据个数多,要给后面新开的容量赋初值
{
while (_finish < _start + n)
{
*_finish = val;
++finish;
}
}
else //如果个数比原来少,就要让尾位置前移
{
_finish = _start + n;
}
}
iterator insert(iterator pos, const T& x) //向容器内插入值(涉及到迭代器失效)
{
assert(pos >= _start);
assert(pos <= _finish);
if(_finish == _end_of_storage) //检查容量是否已满
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iteartor end = _finish - 1; //将pos以及后面的值依次后移
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
void push_back(const T& x) //尾插
{
insert(end(), x);
}
void pop_back() //尾删
{
assert(_finish > _start);
--_finish;
}
iterator erase(iterator pos) //删除pos位置的值(涉及到迭代器失效问题)
{
assert(pos >= _start);
assert(pos < _finish);
iterator begin = pos + 1;
while(begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
}
}
2、迭代器失效问题
导致迭代器失效的原因有两个:
1、扩容导致的野指针
2、pos指向位置已经不是原来的值了
插入扩容导致的迭代器失效:
void reserve(size_t n) //扩容
{
if(n > capacity())
{
size_t sz = size(); //提前记录下数据个数,方便下面更新(涉及到迭代器失效)
T*tmp = new T[n];
if(_start)
{
//memcpy(tmp, _start, sizeof(T)*sz); //同上面所说过的涉及到深浅拷贝
for(size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;//_finish = _start + size();这是错误写法,会导致迭代器失效,_finish会变成野指针
_end_of_storage = _start + n;
}
}
pos指向位置已经不是原来的值了:
这里拿插入和删除各举一个例子:
要求在所有偶数前插入这个偶数本身的二倍:
auto it = v.begin();
while (it != end())
{
if (*it % 2 == 0)
v.insert(it, *it * 2);
++it;
}
这个写法看起来没有问题,但是实际运行的时候便会出现程序崩溃或者一直循环,看下图分析:
所以正确的写法应该是:
auto it = v.begin();
while (it != end())
{
if (*it % 2 == 0)
{
v.insert(it, *it * 2);
++it;
++it;
}
else
++it;
}
删除也是同理:
要求删除所有的偶数:
auto it = v.begin();
while (it != v.end())
{
if(*it %2 == 0)
{
v.erase(it);
}
++it;
}
正确写法应该是:
auto it = v.begin();
while (it != v.end())
{
if(*it %2 == 0)
{
it = v.erase(it);
}
else
++it;
}
3、深浅拷贝导致的程序崩溃问题分析
深浅拷贝导致的程序崩溃本质上就是析构函数被执行了两次,第二次析构函数执行错误。