我有一个名为shape1->overlaps(shape2)
的方法和一个形状矢量。
我必须擦除在我的矢量形状内重叠的所有形状。我目前对如何做到这一点感到茫然,我不确定为什么这段代码不起作用。
for (vector<Shape *>::iterator it = shapes->begin(); it != shapes->end();){
for (vector<Shape *>::iterator jt = it + 1; jt != shapes->end();){
// If the shapes are not the same
if (*it != *jt){
// Check if they overlap, if they do remove them.
if ((*it)->overlaps(*jt)){
// Print the overlapping shapes
printShapeInfo(*jt);
jt = shapes->erase(jt);
} else {
++jt;
}
} else {
++jt;
}
}
printShapeInfo(*it);
it = shapes->erase(it);
++it;
}
我得到的错误是:在运行时期间,矢量迭代器在Visual Studio中不可递增。
答案 0 :(得分:1)
一些建议:
首先,对于外部循环,使用常规循环索引,而不是迭代器。
原因是您在循环时更改了矢量的内容和大小。更改内容意味着您现在正在使用的迭代器将无效。在这方面,使用普通索引要容易得多。
其次,对于内循环,去除它并使用算法函数(或两个)来找出被删除的内容。基本上,你有一个循环。
这是一个可能模仿您尝试做的事情的版本(未经测试)。请注意,我不得不假装你的课程,试图展示正在发生的事情:
#include <vector>
#include <algorithm>
//...
class Shape
{
public:
bool overlaps(Shape *s) { return true; }
bool operator==(const Shape& s) { return true; }
bool operator!=(const Shape& s) { return false; }
};
void printShapeInfo(Shape& s) {}
void foo(std::vector<Shape*>* shapes)
{
// use a regular index here
for (size_t i = 0; i < shapes->size(); ++i)
{
// get our starting point
auto it = shapes->begin() + i;
// point to the next item after the i'th item.
auto it2 = shapes->begin() + i + 1;
// partition the objects. Collided ones go on the left of the partition,
// others go to the right of the partition.
auto div =
std::stable_partition(it2, shapes->end(),
[&](Shape* s){ return (*s != *(*it2))?s->overlaps(*it2):false;});
// check if there is anything on left side of partition
if ( it2 != div )
{
// for each of the collided ones, print stuff out
std::for_each(it2, div, [&](Shape *s){ printShapeInfo(*s); });
// we're done with them, now erase them.
shapes->erase(it2, div);
// also erase the iterator we collided with
shapes->erase(it);
}
}
}
做了什么?我们使用std::stable_partition将碰撞元素移动到向量的一侧,其中div
是碰撞和非碰撞项之间的分界线。
这使我们有机会为每个项目调用printShapeInfo
。然后我们最终使用vector::erase
将它们从向量中移除。
请注意,使用此实现可以消除(希望)迭代器失效错误。该算法仅用于工作&#34;当给出正确的参数时。你也没有看到试图重新安装迭代器的棘手代码。对于序列容器,几乎没有理由编写循环遍历容器的循环,同时从容器中擦除。
同样,这没有经过测试,但你应该了解所做的事情。
答案 1 :(得分:0)
我认为唯一的问题是外循环底部的it++;
。
考虑当向量中只剩下一个形状时会发生什么。你到达循环的底部并删除最后一个形状。这会将it
更新为shapes->end()
。然后,您无法再次递增it
,因为这将离开数组的末尾。在生产代码中,这将是未定义的行为。在调试代码中,看起来Visual C ++提供了一些检测。
删除it++;
语句,您将始终指向下一个形状(如果有),这似乎是您想要的。