【问题标题】:Global Vector Getting Reset全局矢量正在重置
【发布时间】:2019-04-02 09:08:29
【问题描述】:

更新:

正如神经元所建议的,我尝试输出向量的地址,在整个程序中都是一样的。这意味着没有重复发生;在定义 Planet 和 Player 之后但在程序进入 setup() 和 render() 之前,向量以某种方式得到“重置”。这可能是什么原因造成的?

更新 2:

使用 cout,我确定错误发生在 main() 之前。所以全局的 Player 和 Planet 对象被构造出来,指向它们的指针被添加到 MasterEntityVector 中。然后,在 main() 启动之前(或 main() 启动,但在它的任何部分执行之前),MasterEntityVector 被“重置”,之后一切都按预期运行。当 MasterEntityVector 是 CircularEntity 类的静态成员以及 MasterEntityVector 是 OpenGLLoopLogic.ccp 中的全局成员时,都会发生此行为。什么可能导致这种行为?我认为这可能与“静态初始化顺序惨败”http://www.parashift.com/c++-faq-lite/static-init-order.html 有关,但问题似乎略有不同(例如,我没有崩溃)。

更新 3:

我不知道为什么它不起作用,但我找到了有同样问题的人并找到了解决方案。请看下面我的回答。我将删除整个项目的包含(将问题缩减回原来的大小),因为在链接中查看 Alex 的问题时,更大的项目似乎与问题无关。


如果我的错误是微不足道的,我提前道歉。我是一个 C++ 初学者,对范围和多文件项目的概念掌握得比较差,所以虽然我花了很多时间研究代码并在 Internet 上搜索答案,但我可能错过了一些明显的东西。

为了让我的问题更容易回答,下面的代码被简化了。

我正在使用代码块、C++、OpenGL(用于图形)和 SDL(用于窗口)。

为故障排除添加了 cout 行,我将在下面包含输出。

问题是一个名为 MasterEntityVector 的全局向量,它应该包含指向我的模拟中所有“实体”的指针。它在 CircularEntity.ccp 中声明,在 CircularEntity.h 中有一个 extern。应该在实体的构造函数期间将指针添加到它。在 OpenGLLoopLogic.ccp 中,在创建实体时将指针添加到 MasterEntityVector,但是当我们开始进入 init/setup/render 函数时,它似乎要么被重置,要么得到它的第二个实例。如何制止这种不良行为?

CircularEntity.h:

#ifndef CIRCULARENTITY_H
#define CIRCULARENTITY_H

#include "LUtil.h"

class CircularEntity {
    public:
        CircularEntity(double x, double y, int r);
        double xpos, ypos;
        int radius;
        void Draw(double camxpos, double camypos);

};

extern std::vector<CircularEntity *> MasterEntityVector;  //contains pointers to ALL entities

#endif // CIRCULARENTITY_H

CircularEntity.ccp:

#include "CircularEntity.h"

std::vector<CircularEntity *> MasterEntityVector;  //contains pointers to ALL entities

CircularEntity::CircularEntity(double x, double y, int r) {
    radius = r;
    xpos = x;
    ypos = y;
    std::cout << "test 1" << std::endl;
    std::cout << MasterEntityVector.size() << std::endl;
    MasterEntityVector.push_back(this);
    std::cout << "test 2" << std::endl;
    std::cout << MasterEntityVector.size() << std::endl;
}

...
//irrelevant code removed
...

OpenGLLoopLogic.h:

#ifndef OPENGLLOOPLOGIC_H
#define OPENGLLOOPLOGIC_H

#include "MoveableCircular.h"

//Screen constants
const int SCREEN_WIDTH = 1800;
const int SCREEN_HEIGHT = 1000;

bool initGL();
    
void setup();

void update();
    
void render();
    
void handleKeys( unsigned char key, int x, int y );

#endif // OPENGLLOOPLOGIC_H

OpenGLLoopLogic.ccp:

#include "OpenGLLoopLogic.h"

//The projection scale
GLfloat gProjectionScale = 1.f;
MoveableCircular Player(200, 200, 0, 0, .05, 10);
CircularEntity Planet(0, 0, 100);

bool initGL()
{
    ...
    //irrelevant code removed
    ...
    setup();

    return true;
}

void setup() {
    CircularEntity Planet2(0, 0, 100);
    CircularEntity Planet3(0, 0, 100);
}

void velocityupdate()
{
    Player.Gravity(0,0,100);
}

void positionupdate()
{
    Player.PositionUpdate();
}

void update()
{
        velocityupdate();
        positionupdate();
}

void render()
{
    ...
    //irrelevant code removed
    ...
    for (int n=0; n<MasterEntityVector.size(); n += 1) {
        (*MasterEntityVector[n]).Draw(Player.xpos, Player.ypos);
        std::cout << MasterEntityVector.size() << std::endl;
    }

    ...
    //irrelevant code removed
    ...
}

void handleKeys( unsigned char key, int x, int y )
{
    ...
    //irrelevant code removed
    ...
}

我省略了几个文件,所以你们都不必阅读大量不相关的代码:

MoveableCircular 源和标头与 CircularEntity 文件非常相似。 (cout 的测试 3 和 4 而不是 1 和 2,MoveableCircular 类继承自 CirularEntity,它只是有一个重新定义的构造函数)。 main.ccp 调用 init,然后有一个循环:处理键,更新,然后渲染。 “包含树”中“高于”MoveableCirular.h 的文件(我不知道正确的术语)不应该与这个问题有任何关系,他们所做的唯一与这个问题真正相关的是“#包括“

输出是:

test 1
0
test 2
1
test 3
1
test 4
2
test 1
2
test 2
3
test 1
0
test 2
1
test 1
1
test 2
2
2
2
2
.
.
.
[infinite 2's]

从输出中可以看出,在构造 Player 和 Planet 对象时一切正常。 但是,当我们进入 OpenGLLoopLogic 函数(设置中的 Planet2 和 Planet3、绘制代码渲染等)时,它似乎“重置”或创建了 MasterEntityVector 的第二个副本。这种不良行为的原因是什么?

我已经尝试过的事情:

在 MasterEntityVector 之前的整个代码中添加“::”

命名空间的东西(虽然我对命名空间的知识和理解确实很薄弱,所以这仍然可能是问题的根源)。

【问题讨论】:

  • 这里发生了很多事情,而且非常难以阅读。您能否提供一个尽可能简短的说明问题的最小示例?
  • @Dan 感谢您花时间查看它!我知道它很长。您是否阅读过注释“如果这是您第一次阅读此内容,您需要回答问题的所有信息可能都在上面...”如果是,请再次阅读该注释;p I '我很确定错误在那个注释之上。其他一切都在那里,只是因为杰克要求看。至于该注释上面的所有内容,我想我可以尝试进一步缩短它,但这会很困难,为了简洁起见,我已经花了很多时间进行编辑,并且还有很多剩余的代码(例如对象和 cout 行)在那里用于故障排除!
  • “::MasterEntityVector”和“Player.MasterEntityVector”是一样的吗?它们看起来不像。
  • 另外——你有没有用调试器完成这个?使用 Visual Studio 或 ddd,您应该能够在几分钟内观察变化并缩小范围。
  • @kfsone 很抱歉造成混淆,::MasterEntityVector 是来自 OpenGLLoopLogic.ccp 的全局变量,根据 Jack 的建议,我将其更改为 CircularEntity 类的静态成员。这似乎根本没有影响问题(所有 cout 都在同一个地方做同样的事情)。如上所述的问题仍然有原始的“来自 OpenGLLoopLogic.ccp 的全局”版本的向量,而不是新的“CircularEntity 类的静态成员”。 Player.MasterEntityVector 是新版本(当我需要从 OpenGLLoopLogic.ccp 访问它时)。

标签: c++ global


【解决方案1】:

我仍然不知道为什么我做错了,但这里有一个相同问题的链接,并为遇到类似问题的人提供了修复:

Global vector emptying itself between calls?

查看 ZeRemz 的解决方案。

就我而言,我使用/添加了以下代码:

在 CircularEntity.h 中:

std::vector<CircularEntity *> &getMasterEntityVector();

在 CircularEntity.ccp 中:

std::vector<CircularEntity *> &getMasterEntityVector()
{
    static std::vector<CircularEntity *> s_vector;
    return s_vector;
}

我仍然不知道为什么我的原始实现(或者为什么 Alex 在链接中的原始实现)是错误的,而且我从不喜欢不知道 为什么,但在至少我们有一个解决方案!

【讨论】:

  • 哦——听起来问题出在施工顺序上;当 premain 终于开始初始化 MasterEntityVector 本身时,您已经开始通过其他全局对象的构造函数使用 MasterEntityVector。
  • @kfsone 是的,我认为是这样的。我认为这没关系,因为我在构造函数中使用 cout 访问 MasterEntityVector 以确保向量在那里并且工作。我怎么可能在没有初始化的情况下对 MasterEntityVector() 调用 .size() 之类的事情? (并且它总是会输出正确的输出:例如,使用 .size 调用它会为 1 个对象输出 1,为 2 个对象输出 2,为 3 个对象输出 3……无论正确的数字是什么……直到它被重置在 main()) 之前
  • 这就是构造和初始化的区别;该对象被构造 - 它存在于内存中,因为作为全局确保它在数据部分中保留了内存,但它还没有被初始化,它只有默认的空白值。更重要的是:它还没有被初始化 - 应用程序的 pre-main 仍然在通过待办事项列表工作。
  • 哈哈我不喜欢这样做,但有道理,非常感谢您的解释!
  • 澄清:显然,像其他所有东西一样,它需要被构造然后初始化,但我不喜欢它在初始化之前功能齐全,然后在没有任何错误或警告的情况下重置。当它在初始化之前完全起作用时,初始化类似的东西有什么意义?!?要么我误解了某些东西,要么这里的某些东西看起来更像是一个错误而不是一个功能......
【解决方案2】:

忘记任何命名空间中的extern 变量,并在CircularEntity 类(或其他类,如Entities)中使用static 变量。

//CircularEntity.h:
class CircularEntity {
  public:
    static vector<CircularEntity*> entities;
}

//CircularEntity.cpp
vector<CircularEntity*> CircularEntities::entities;

...
CircularEntities::entities.push_back(whatever);

这样一切都会更加封装。

【讨论】:

  • 感谢您的快速回复!我觉得我们离答案更近了,但基本问题仍然存在。我实现如下: //CircularEntity.h: class CircularEntity { public: static std::vector MasterEntityVector; //包含指向所有实体的指针 //CircularEntity.cpp: std::vector CircularEntity::MasterEntityVector; //这一行在任何函数之前。我还删除了 cout 测试 3 和 4,因为我意识到可移动对象正在通过两个构造函数并被添加到向量中两次。 (下续)
  • 同样的问题仍然存在。 MasterEntity Vector 似乎“重置”和/或在同一位置获取另一个副本。新的输出是:test 1 0 test 2 1 test 1 1 test 2 2 test 1 0 test 2 1 test 1 1 test 2 2 2 ...
  • 您可以看到“重置”或任何问题仍然发生在以前的相同位置,尽管向量现在是 CircularEntity 类的静态成员而不是来自 OpenGLLoopLogic.ccp 的全局变量
  • 那么您发布的代码中一定隐藏了一些我们缺少的东西。
  • 如果是这种情况,我很抱歉浪费您的时间;我刚刚添加了我最初遗漏的所有文件。非常感谢您一直以来的帮助!
【解决方案3】:

我已经阅读了几乎所有代码,但仍有一些问题:

  • 在您的代码中,MasterEntityVector 仅在 CircularEntity 的构造函数中被引用。是否在其他任何地方被引用过,尤其是它的 pop_backerase 或任何调用的非 const 方法?
  • CircularEntity 及其子类的对象,它们是在哪里构造的?
  • CircularEntity::~CircularEntity 没有过载,是吗?

对于后两个问题,我在

中发现了一个错误(?)
void setup() {
    CircularEntity Planet2(0, 0, 100);
    CircularEntity Planet3(0, 0, 100);
}

您已经在本地构建了 2 个 CircularEntitys,因此在 setup() 调用 initGL() 后它们将被破坏。 如果你正确写了~CircularEntity,你必须从MasterEntityVector中删除this,从而减小向量的大小。 (但我没有看到~CircularEntity的声明)

此外,如果您怀疑是否有另一个实例,我认为您可以尝试输出全局向量的地址。

【讨论】:

  • 根据 Jack 的建议,我将向量设为 CircularEntity 类的静态成员,而不是 OpenGLLoopLogic.ccp 中的全局成员。就结果而言,没有改变任何东西。 1) MasterEntityVector 在别处被引用,但没有调用它的方法,它只是调用 MasterEntityVector.size() 和 std::cout
  • 2) CircularEntity 的对象在 OpenGLLoopLogic.ccp 中构造。 Player 和 Planet 就在开头,另外 2 个在 setup() 中。 (关于那个错误的好电话,你是对的。我不认为这是问题所在吗?) 3)不,我什至还没有制作解构函数。
  • 我尝试按照您的建议输出向量的地址,在整个程序中都是一样的。这意味着没有重复发生;在定义了 Planet 和 Player 之后但在程序进入 setup() 和 render() 之前,向量以某种方式得到“重置”。这可能是什么原因造成的?
猜你喜欢
  • 2016-04-05
  • 2013-04-18
  • 1970-01-01
  • 1970-01-01
  • 2015-12-04
  • 1970-01-01
  • 2020-07-23
  • 2016-07-13
  • 2023-03-04
相关资源
最近更新 更多