赞
踩
送给大家一句话:
世界在旋转,我们跌跌撞撞前进,这就够了 —— 阿贝尔 加缪
我们之前实现了手搓vector,但是当时依然有些问题没有解决:
接下来,我们一点一点来解决这些问题!!!
来看这个这个构造函数:
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
first++;
}
}
这个是对迭代器区间进行的构造函数,思路很简单,把迭代器区间的数据依次尾插就可以了(这里之所以另外使用一个新的模版,而不是使用vector类的模版,是为了兼容更多的数据类型)。这样就可以通过一个现有的类型来构造容器。
但是出乎意料的是出现了一个问题: C2100 非法的间接寻址
(编译层面的问题) 。非法的间接寻址的造成原因有很多:
我们分析一下我们遇到的问题是那种问题?空指针引用吗?不可能!野指针引用吗?也不可能!!! 那么真相只有一个:我们遇到了类型不匹配
的问题,那这是来自哪里的呢???,经过我的排除法(注释不同的代码块来进行查找),得到了结果
vector<int> v1(5,6);
这一行代码是我们出错的根源,为什么这个构造没有去使用vector(size_t n,T val = T())
,而是使用我们的vector(InputIterator first, InputIterator last)
,因为第二个函数与(5,6)的类型更匹配,编译器会寻找最合适的函数。
解决方法也是十分暴力:多枚举几个 构造函数:
vector(size_t n,T val = T())
vector(int n,T val = T());
vector(long long n,T val = T());
这样就会优先匹配vector(int n,T val = T());
了,我们的问题也就解决了。
这个问题主要出现在我们的插入操作(insert)和删除操作(erase)。来看:
void vector_test7() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); v1.push_back(6); v1.push_back(7); vector<int>::iterator it = v1.begin() + 3;// 4 cout << *it << endl; v1.insert(3, 40); cout << *it << endl; }
这个执行的结果是:
迭代器的指向发生了改变,我们实现的迭代器的底层是指针,我们插入之后指针位置不变,而数组元素改变,自然会产生不一样的结果。这个问题看起来不严重,那我们再来看:
void vector_test7() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); v1.push_back(6); v1.push_back(7); v1.push_back(8); vector<int>::iterator it = v1.begin() + 3;// 4 cout << *it << endl; v1.insert(3, 40); cout << *it << endl; }
为什么这里出现了乱码???我们代码和之前的区别是什么???一个进行了扩容,一个没进行扩容。扩容之后vector的_start发生了改变,自然我们的指针也失去了对应作用。 迭代器就失效了,这个解决办法也很简单,就是插入之后不要使用之前的迭代器!!!一定要对迭代器进行更新。
再来看erase中的问题:
void vector_test7() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); //v1.push_back(6); //v1.push_back(7); //v1.push_back(8); vector<int>::iterator it = v1.begin(); //删除偶数 while (it != v1.end()) { if (*it % 2 == 0) { v1.erase(it); } ++it; } print_vector(v1); }
这样运行起来是没有问题的,那么再来看:
void vector_test7() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(4); v1.push_back(5); //v1.push_back(6); //v1.push_back(7); //v1.push_back(8); vector<int>::iterator it = v1.begin(); //删除偶数 while (it != v1.end()) { if (*it % 2 == 0) { v1.erase(it); } ++it; } print_vector(v1); }
现在出现了:
这个问题,问题的来源也很简单,我们迭代器在删除之后没有改变位置,但是_start的元素发生了改变,也就是相当于 it 向后移动了两次,为了避免这个情况我们可以:
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
else
{
++it;
}
}
这样就可以了:
需要注意的一点是,我们的操作是以g++标准来进行的(如果删除会进行缩容,也会出现错误,迭代器就不能进行++了),所以 在VS环境下,vector 容器在erase 之后的迭代器是严格不能使用的,使用就会报错
,因为VS迭代器的底层不是原生指针,判断有所不同。
迭代器失效解决方案总结:
1. 删除插入之后更新对应迭代器!(erase删除后会返回新的迭代器 ,按规则进行迭代就可以了 it = v1.erase(it)
)
2. 插入删除之后不使用迭代器
我们创建一个string类的容器,来看看能不能正常运行:
void vector_test8() {
vector<string> v1;
v1.push_back("11111");
v1.push_back("22222");
v1.push_back("33333");
v1.push_back("44444");
v1.push_back("55555");
print_vector(v1);
}
来看效果:
程序直接崩掉了,经过我们的调试,我们能打印出来正确的数据,但是走到程序最后的时候出现了错误,那么应该就是析构函数的问题了!
来画图分析一波:
结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃
那么怎么解决呢???非常简单:
//扩容 void reserve(size_t newcapacity) { //记录位置 size_t n = _finish - _start; T* tmp = new T[newcapacity]; //拷贝 //memcpy(tmp, _start, size() * sizeof(T)); for (size_t i = 0; i < size(); i++) { tmp[i] = _start[i]; } delete[] _start; _start = tmp; _finish = _start + n; _end = _start + newcapacity; }
不使用memcpy函数不就可以了,然后我们使用简单粗暴的赋值拷贝,这样就不会发生浅拷贝问题了!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。