擦除CPP中的重叠对象

时间:2015-11-17 02:12:24

标签: c++ vector stl

我有一个名为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中不可递增。

2 个答案:

答案 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++;语句,您将始终指向下一个形状(如果有),这似乎是您想要的。