【问题标题】:Overflowing the stack memory with my quadtree class and don't know why用我的四叉树类溢出堆栈内存,不知道为什么
【发布时间】:2021-11-26 19:26:03
【问题描述】:

我一直在研究四叉树类以检测可能的碰撞,但我添加的项目越多,堆栈溢出的速度就越快。

我还注意到,随着程序运行,内存使用量呈线性增加,所以我认为我没有正确删除某些内容,它只是相互堆叠?

我知道大多数堆栈溢出错误是由递归引起的,但它不应该只发生在 100 个对象上。

#include "Quadtree.h"

Quadtree::Quadtree(Rectangle* bounds)
{
    this->bIsSplit = false;
    this->bounds = bounds;
    this->maxObjects = 5;
}

void Quadtree::Split(std::vector<std::pair<SpaceObject*, SpaceObject*>> &collidingObjects)
{
    float x = bounds->x;
    float y = bounds->y;
    float subWidth = bounds->width / 2;
    float subHeight = bounds->height / 2;

    // Top Left
    nodes.push_back(new Quadtree(new Rectangle(x, y, subWidth, subHeight)));
    // Top Right
    nodes.push_back(new Quadtree(new Rectangle(x + subWidth, y, subWidth, subHeight)));
    // Bottom Left
    nodes.push_back(new Quadtree(new Rectangle(x, y + subHeight, subWidth, subHeight)));
    // Bottom Right
    nodes.push_back(new Quadtree(new Rectangle(x + subWidth, y + subHeight, subWidth, subHeight)));

    // takes all the objects in the parent node and splits them into there corresponding subdivided quadtree
    for (auto o : this->objects)
    {
        for (int i = 0; i < nodes.size(); i++)
        {
            if (nodes[i]->bounds->Contains(o->x, o->y, o->size))
            {
                nodes[i]->Insert(o, collidingObjects);
            }
        }
    }

    this->objects.clear();
    this->bIsSplit = true;
}

void Quadtree::Insert(SpaceObject* spaceObject, std::vector<std::pair<SpaceObject*, SpaceObject*>> &collidingObjects)
{
// if the object does not fit inside this quadtree, this isn't the right quadtree
if (!bounds->Contains(spaceObject->x, spaceObject->y, spaceObject->size))
{
    return;
}

if (!bIsSplit && this->objects.size() < maxObjects) // add the object to the quadtree if the max has not been hit
{
    objects.push_back(spaceObject);

    for (auto o : this->objects)
    {
        // if the objects are not the same and overlapping (pythag)
        if (spaceObject != o &&
            ((spaceObject->x - o->x) * (spaceObject->x - o->x)) + ((spaceObject->y - o->y) * (spaceObject->y - o->y)) <= (spaceObject->size + o->size) * (spaceObject->size + o->size))
        {
            // Add colliding pair to the collidingObjects vector
            collidingObjects.push_back(std::make_pair(spaceObject, o));
        }
    }

}
else
{
    if (!this->bIsSplit) // splits the quadtree if this quadtree has not been split yet
    {
        this->Split(collidingObjects);
    }

    // adds the passed in object to one of the subnodes
    for (auto n : nodes)
    {
        n->Insert(spaceObject, collidingObjects);
    }
}
}

void Quadtree::Delete(SpaceObject* spaceObject)
{
    if (!bounds->Contains(spaceObject->x, spaceObject->y, spaceObject->size))
    {
        return;
    }

    if (!bIsSplit)
    {
        for (int i = 0; i < objects.size(); i++)
        {
            if (objects[i] == spaceObject)
            {
                this->objects.erase(objects.begin() + i);
                return;
            }
        }
    }
    else
    {
        for (auto n : nodes)
        {
            n->Delete(spaceObject);
       }
    }
}

void Quadtree::Clear()
{
    if (bIsSplit)
    {
        for (auto n : nodes)
        {
            n->Clear();
        }
        this->bIsSplit = false;
    }
    nodes.clear();
    objects.clear();
}

【问题讨论】:

  • 如果它们完全重合,即使场景中只有两个对象也可能导致四叉树遍历中的无限递归(如果您以非递归方式编写代码,则会导致无限循环)。您通常需要对此类空间索引进行一些最大深度限制或有效限制最大深度的最小像元大小。无意粗鲁,我认为在您的水平上,使用空间散列比四叉树要好得多。确定单元格大小后,所涉及的摆弄就更少了,我真的认为您会从中获得更好的结果,直到您开始掌握事情的窍门。

标签: c++ recursion stack-overflow


【解决方案1】:

好吧,我不知道nodes 是什么或Quadtree 是如何定义的,因为您没有显示完整的代码。但是您使用new 是可疑的。 nodes 是指针的集合吗?如果是这样,您可能会将它们丢在地板上,因为它们永远不会被删除。


不要到处写this-&gt;。成员在成员函数的范围内。您的Quadtree 构造函数应该为常量成员使用内联初始化程序,并为bounds 使用初始化程序列表。而且,在这里使用裸指针是一个危险信号。这个bounds 的所有权是什么?为什么它需要是指针而不是简单的 Rectangle 类型的值?

我想知道你是否习惯了另一种语言,一种使用new 进行所有构造的语言,并且具有引用语义并且对象实际上是指针。 C++ 不同。

我建议在不使用任何指针的情况下编写它。当然,Rectangle 应该是一个值。如果元素可以有效地移动Quadtree 的向量可以在插入和删除等时有效。所以,Quadtree 应该有一个移动构造函数。如果您还不完全了解构造函数和特殊成员,这可能是一个高级概念。

即使是(尤其是)初学者,您也绝对应该遵循 ⧺C.149 — 不是赤裸裸的 newdelete。因此,如果您需要创建一个指向Quadtree指针 向量,请将其设为shared_ptr 的向量。请记住,您在 C++ 中没有垃圾收集,因此任何使用指针的东西都必须明确处理生命周期管理的责任。 shared_ptr 的行为会更像你习惯的那样,并且它们可以安全地用于像 vector 这样的容器中。

【讨论】:

  • 我习惯使用 C#,但 nodes 是 quadtree* 的向量,bounds 是 quadtree 类中的 Rectangle 结构。对此感到抱歉。
  • 好的,是的,摆脱所有指针并使用值语义。关注⧺C.149 — 没有裸体newdelete。值应该适合矩形。如果您需要向量是指针以提高效率,请使用shared_ptr。更好的是,通过提供 move 语义使其不会低效。
  • 所以“节点”向量是内存泄漏,切换解决了我的内存问题。但我似乎仍然几乎随机地出现堆栈溢出。您是否看到递归循环可能不会退出的任何条件?
  • @SamHirsch 我建议(在清理代码和格式之后)添加一些打印语句,以便您可以看到递归在做什么。
猜你喜欢
  • 2017-04-24
  • 2012-12-19
  • 2014-12-07
  • 2012-05-10
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-01
相关资源
最近更新 更多