对象指针向量的元素 K 在将第 N 个元素从元素 K-1 推入向量后变为空

Posted

技术标签:

【中文标题】对象指针向量的元素 K 在将第 N 个元素从元素 K-1 推入向量后变为空【英文标题】:Element K of an object pointer vector turns null after pushing N-th element into the vector from element K-1 【发布时间】:2019-05-17 16:39:22 【问题描述】:

我最近开始学习 OpenGL。我有一个测试我的技能的想法在 learnopengl.com 教程的每个部分之后,我目前正在学习章节 https://learnopengl.com/Getting-started/Shaders

所以我创建了一个简单的乒乓球游戏,在调试时我注意到了奇怪的行为。 我创建的游戏会在一段时间后崩溃,但总是在其中一名玩家得分之后。 需要注意的是,每个游戏对象都有一个更新函数,我在每一帧都调用它,并且我将指向所有游戏对象的指针保存在 STL 向量中。

我分析了一些代码并没有发现任何问题,所以我启动了 gdb 并写了一些“std::cerr”信息。 这是我发现的: 1. 更新其中一个对象时游戏崩溃 (obj->update()) 2. 玩家获得新积分后游戏总是崩溃,使得游戏对象指针向量包含32个元素。如果我在游戏开始前使用 GAMEOBJECTS.relocate(33),则向量在崩溃时包含 33 个元素。 3. 更新向量中的第五个游戏对象时游戏崩溃。这里必须注意的是,球对象是第四个,Ball::update() 包含创建新游戏对象(点)的代码。 从这些信息中我推断,当我创建一个新对象并将它的指针推送到一个向量时,向量必须重新定位自己,因为它没有足够的容量。 反过来,这会导致一些我不理解的奇怪的记忆混乱,因为我刚刚开始学习 C++ 编程。 这也是无法访问向量的第五个元素的原因。 我正在努力寻找解决此问题的方法,但我最担心的是了解为什么会发生这种情况。

代码太大,这里就不贴了,所以放到github上:https://github.com/carul/opengl_pong

我仍然会描述我认为最重要的内容:

for(auto && obj : GAMEOBJECTS)
  obj->update();
  obj->draw(VBO);

这就是我用来绘制和更新游戏对象的循环。 我认为它没有什么特别之处,除了它一个接一个地更新对象。

void Ball::update()
   ///...
   if(boundingLeft < -0.96f)
    this->setPosition(0.0f, 0.0f);
    this->speedmodif = 0.8f;
    this->angle = 0;
    Point * point = new Point(GL_STATIC_DRAW);
    point->create(POINT_DATA);
    point->indice(POINT_INDICES);
    point->setPlayer(2);
    point->name = "POINT";
    point->setPosition(0.93f - 0.03f*scorep2, 0.9f);
    scorep2++;
    GAMEOBJECTS.push_back(point);
  
  if(boundingRight > 0.96f)
    this->setPosition(0.0f, 0.0f);
    this->speedmodif = 0.8f;
    this->angle = M_PI;
    Point * point = new Point(GL_STATIC_DRAW);
    point->create(POINT_DATA);
    point->indice(POINT_INDICES);
    point->setPlayer(1);
    point->name = "POINT";
    point->setPosition(-0.95f + 0.03f*scorep1, 0.9f);
    scorep1++;
    GAMEOBJECTS.push_back(point);
  
  ///...

这是小球更新功能的一个片段。这段代码负责创建一个新点。 Ball 是 Object 类的派生类。 GAMEOBJECTS 是这样定义的:std::vector&lt;Object * &gt; GAMEOBJECTS;

抱歉,帖子太长了,我试图提供我所拥有的所有信息。 非常感谢您阅读本文的这一部分,并感谢您提供的任何帮助 下面是崩溃时gdb和游戏的截图:

【问题讨论】:

你不保存对向量元素的引用,将元素推送到向量然后使用该引用吗?如果向量必须重新分配存储以增加其容量,则所有引用和迭代器都将失效。 首先:您正在泄漏内存。对于初学者,我建议不要使用关键字“new”。如果需要指针,请改用 unique_ptr。至于崩溃,我认为它可能与可能在 push_back 上发生的向量的重新分配有关。如果没有看到可靠的代码,就无法确定。 “保存”是什么意思?我正在创建一个新对象并将指向它的指针推送到向量。然后删除指针,因为无论如何我都使用向量访问所有元素,所以我不需要它。 @skeller 好吧,这并不是什么新鲜事,我在帖子中说过,我也认为这与重新分配有关。但是你能告诉我我在哪里泄漏内存吗?我想修复它。 我只知道操作系统无论如何都会删除它们。 -- 然后,以后的要求可能会要求您将该代码移动到被调用的程序的一部分一个循环。您的代码应该准备好从程序的任何部分调用,并且不会失败。依靠操作系统来纠正你的“错误”并不是编写程序的好方法。 【参考方案1】:

在这个循环中

for(auto && obj : GAMEOBJECTS) 
    obj->update();
    obj->draw(VBO);

obj 是对向量元素的引用。在update()push_back() 变成GAMEOBJECTS。如果向量的容量不足以容纳新元素,则此操作会使obj 引用无效。

【讨论】:

谢谢!实际上,在阅读了您的评论后,我将自动循环替换为“for(unsigned i = 0; i @Cuaox,我建议不要在迭代向量时修改它。这样的代码很难推理并且容易出错。 是的,您是对的,您是否有任何关于游戏引擎如何做到这一点的信息?还必须更新东西,我看到很多人在更新步骤中添加实体 @Cuaox,我的评论不是关于算法,而是关于代码。如果需要,您可以在更新期间添加条目,但我不喜欢您这样做的方式:obj-&gt;update() 修改 GAMEOBJECTS 对读者来说并不明显。要找到错误,必须仔细检查update() 在内部做了什么。这里没有更具体的空间。我建议你在 SO Code Review 上询问他们对这段代码的看法以及如何改进它。

以上是关于对象指针向量的元素 K 在将第 N 个元素从元素 K-1 推入向量后变为空的主要内容,如果未能解决你的问题,请参考以下文章

指向列表向量中第 n 个元素的指针

算法基础--直接插入排序

在删除指向动态分配对象的指针向量中的元素之前,我需要做啥?

访问指向对象指针向量的指针的第一个元素?

将指针向量元素推回非指针向量c ++时出错

搜索k个最近的元素[重复]