擦除CPP中的重叠对象

Erasing Overlapping Objects In CPP

本文关键字:重叠 对象 CPP 擦除      更新时间:2023-10-16

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

一些建议:

首先,对于外部循环,使用常规循环索引,而不是迭代器。

原因是您在循环时更改了向量的内容和大小。更改内容意味着您现在用来循环的迭代器将无效。在这方面,使用普通索引要容易得多。

其次,对于内部循环,去掉它,并使用一个(或两个)算法函数来找出被擦除的内容。所以本质上,你有一个循环。

这里有一个版本(未测试),可能会模仿你想要做的事情。注意,我不得不模拟你的类,试图展示正在发生的事情:

#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将它们从向量中移除。

请注意,迭代器无效错误可以通过此实现消除(希望如此)。当给出正确的参数时,算法的功能只是"工作"。您也不会看到试图重新封装迭代器的棘手代码。对于序列容器,几乎没有理由编写循环来遍历容器,同时从容器中擦除。

同样,这并没有经过测试,但你应该了解所做的事情的要点。

我认为唯一的问题是外循环底部的it++;

考虑一下当向量中只剩下一个形状时会发生什么。你到达循环的底部并擦除最后一个形状。这将it更新为shapes->end()。然后不能再次递增it,因为这会偏离数组的末尾。在生产代码中,这将是未定义的行为。在调试代码中,Visual C++似乎提供了一些检测。

删除it++;语句,您将始终指向下一个形状(如果有的话),它似乎就是您想要的。