vector模拟实现_云的小站的博客-CSDN博客
目录
主题:
迭代器失效
Insert导致的迭代器失效
ereas导致的迭代器失效
非法的间接寻址
深拷贝中的浅拷贝。
主题:
1)迭代器失效
2)非法的间接寻址
3)深拷贝中的浅拷贝
迭代器失效
什么是迭代器失效,在vector里我们的迭代器其实就是指针
template<class Ty>
class vector
{
public:
typedef Ty val_type;
typedef val_type* iterator;
//.....
所以在vector的迭代器失效其实是发生了野指针的问题。
失效发生常发生在Insert与ereas对pos的多次访问中。
Insert导致的迭代器失效
代码bug不考虑
这是STL库里面的vector的报错
我们自己实现的(9条消息) vector模拟实现_云的小站的博客-CSDN博客
发现第二次使用insert插入数据的时候,无论是库还是自己实现都不被允许,这是为什么呢,干嘛调试观察代码
插入前pos地址在d的有效数据范围内(start~finish)
插入后pos位置未改变,但是d的数据存放的位置改变。所以这个时候在下一次的Insert将失败
看看库里的流程
pos也成为了野指针,为什么呢?
在vector扩容都是异地扩容,当Insert后原来的空间被释放,而pos指向的为原来的空间,当第一次Insert内发生了扩容,形参pos改变插入了数据,但是实参pos不被修改。第二次Insert插入时检测出了pos为野指针报错。就算不报错,我们的插入操作也是失效的。扩容导致的迭代器失效。
ereas导致的迭代器失效
那么ereas没有扩容为什么也会迭代器失效呢??有的STL实现在删除操作时,会调用缩容的操作,当缩容后就会导致pos位置失效。但是一般不会做缩容的操作,我们知道就好。
但是ereas的失效主要体现在使用中,
题目:删除数组中的偶数。
乍一看没什么问题但是数据一旦改变!
进程崩溃:
结果错误:
为什么呢?
崩溃原因是,it超出范围。
数组1 2 3 4
将2删除,然后it++
将4删除 戏剧性的一幕发生了,
原本it现在改于end()比较了,但是我们的it
必须先++后比较,避开了循环结束条件。
这时候【】是end的位置,it完美错过结束判断。导致了it下一次在ereas的非法访问。
while (it != d.end())//这里的it>end().
结果错误原因:删除后出现一次跳过。
将删除数据2
++it(未判断移动到当前位置的数据
就贸然it++,忽略了it当前的数据是否需要删除)
结果有误!!!!。
结论:如果最后数据为删除程序崩溃,如果两个偶数连在一起,程序结果有误。
而12345的数据数组,恰好避开了2种错误。
那怎么才能对呢??
接收ereas返回值重新定义it值,it自增需要条件。vector::ereas在库中实现返回删除位置的下一个数据,在vector中其实就是返回pos的位置
代码改为。
while (it != d.end())
{
if (*it % 2 == 0)
{
it = d.ereas(it);
}
else
{
++it;
}
}
这样我们的结果都可以正确了
结果都是正确的。
对于迭代器失效的问题的根本解决方案就是:不要多次直接使用pos数据!!未改变的pos只能用一次!!
非法的间接寻址
讲一个故事:你手上有一个香蕉,有2只猴子,怎么在不破坏香蕉的情况下,2只猴子都有香蕉吃?
答案是:再去摘一根香蕉......
我们实现构造函数中的一种重载函数:使用N个type初始化对象。
vector(size_type n, const Ty&val= Ty());
但是还有个重载的构造函数
template <class InputIterator>//可以是其他容器的迭代器
vector(InputIterator begin, InputIterator end)
如果我们这样创建vector对象
vector<int> d1(5, 6);
//报错!!: error C2100: 非法的间接寻址
为什么呢?编译器误调用了vector(InputIterator begin, InputIterator end);
这不是迭代器调用吗?虽然说明是迭代器的构造,但是这是模板函数,int,int也可以调用该函数,导致了,编译器误把对int类型数据解引用,操作错误!!
如何修改呢??我们不可以改变vector(size_type n, const Ty&val= Ty());的数据,但是我们可以重载一个int n,const...的构造函数
vector(size_type n, const Ty&val= Ty());
vector(int n, const Ty&val= Ty()); //重载size_t n,
//vector(long long n, const Ty&val= Ty());//重载size_t n,int n
这样编译器就会调用int n的函数,而不是调用迭代器构造函数。我们不改变size_t n 但是我们重载了int n 的函数。
深拷贝中的浅拷贝。
深拷贝是深拷贝,浅拷贝是浅拷贝,什么是深拷贝中的浅拷贝???
二维数组的普通拷贝其实就是深拷贝的浅拷贝
三幅图理解!!
浅拷贝
这是二维数组的浅拷贝,直接拷贝了src中的地址。
深拷贝的浅拷贝
我们将二维数组拷贝了一份给des,但是二维数组里面的一维数组地址,我们只是浅拷贝,拷贝了src二维数组中的地址值,而没有根据这些地址值,去拷贝所对应的一维数组。
深拷贝的深拷贝
深拷贝二维数组,本且根据原二维数组中的多个一维数组进行深拷贝。
所以我们在实现vector的拷贝逻辑时的reverse与拷贝构造,不可以使用mem*函数进行字节拷贝,而需要自己实现拷贝逻辑
//1
vector(const vector<val_type>&src)
{
_start = new val_type[src.capacity()];//也可以src.size(),二维数组时的第一层数组开辟空间
//memmove(_start, src._start, sizeof(size_type) * src.size());
// 自定义类型会极可能报错!!!
for (size_t i = 0; i < src.size(); ++i)
{
_start[i] = src._start[i];//如果是自定义成员,会调用他的赋值拷贝,再次进行深拷贝!!!!
//而普通内置类型也不会发生错误,完成赋值拷贝
}
_finish = _start + src.size();
_end_of_storage = _start+src.capacity();//和上面同步
}
这里的“=”如果左右是自定义类型,就会调用他的赋值重载函数,如果是内置类型,就是编译器的事情了
ps:无论什么样的指针,内置类型指针,自定义类型指针,都属于内置类型!!(都是指针)