5

所以我正在用传送和通常的老鼠制作一个蛇游戏。我有一个这样的循环运行:

while(snake.alive() && miceEaten < micePerLevel)
{
    displayInfo(lives, score, level, micePerLevel - miceEaten);
    //some code
    if(miceEaten())
    {
        //update score...
    }
    //more stuff...
}

上面代码的问题是displayInfo在分数更新之前被调用,所以在吃掉鼠标之后,用户必须等到循环再次运行才能看到他的分数更新。所以我把那一行代码移到函数的底部:

while(snake.alive() && miceEaten < micePerLevel)
{
    //some code
    if(miceEaten())
    {
        //update score...
    }
    //more stuff...
    displayInfo(lives, score, level, micePerLevel - miceEaten);
}

和传送停止工作!每当蛇到达传送点时,程序就会崩溃。并displayInfo使用以下代码:

stringstream s;
s << "LEVEL " << left << setw(12) << level << "LIVES: " << setw(12) << lives << "MICE LEFT: " << setw(12) << miceLeft
    << "SCORE: " << setw(13) << score;
printLine(0, s.str(), WHITEONBLUE);

其中printLine只有color_set,mvprintwrefresh(). 与传送无关。奇怪的。

所以我去了蛇功能,蛇从传送中获取下一个位置:

    body.push_back(teleports[overlap(next)]->teleportFrom(dir)); //next is a Location object

Whereteleports[overlap(next)]->teleportFrom(dir)返回蛇将被传送到的位置。为了了解它崩溃的原因(可能Teleport是在屏幕外返回某个位置?),我在上述行之前添加了以下 3 行:

    Location l = teleports[overlap(next)]->teleportFrom(dir);
    mvprintw(1, 0, "(%i, %i)", l.x, l.y);
    refresh();

问题消失了!

不仅如此,我还必须拥有这三行。如果我注释掉mvprintw(1, 0, "(%i, %i)", l.x, l.y);refresh();,或两者都注释掉,程序在到达传送点时会像以前一样崩溃。

关于可能导致这种行为的任何想法?

更新:我尝试删除所有警告(主要是关于比较有符号/无符号数字的警告),但到目前为止只剩下 1 个:

warning: reference to local variable 'other' returned

和代码:

Location& Location::operator = (Location other)
{
    if(this == &other)
        return other;
    x = other.x;
    y = other.y;
    return *this;
}

我该怎么做才能解决此警告?

4

4 回答 4

8

像这样构建你的赋值运算符:
你应该总是返回 *this (即使它们相等)。但他们永远不会,因为您正在创建本地副本(所以这不是您的错误)。

Location& Location::operator = (Location const& other)
{
    // Does it really matter if you assign to self?
    x = other.x;
    y = other.y;
    return *this;
}

对于这样一个简单的类来说,标准的复制和交换似乎有点矫枉过正。

PS。您应该修复所有警告(即使它们与无符号不匹配一样简单)。如果您不修复它们,您将对它们的效力免疫,并且不会发现真正的问题,因为它被您忽略的警告所包围。所以修复它们(我总是打开使编译器将所有警告视为错误的标志,这样如果有任何警告,代码就不会编译)。

实现赋值运算符的正确方法(或最普遍接受的好方法)。是使用复制和交换成语:

// notice the parameter is passed by value (i.e. a copy).
// So the copy part is aromatically taken care of here.
// So now you just need tom implement the swap() part of the idiom.
Location& Location::operator = (Location other)
{
    this->swap(other);
    return *this;
}

void Location::swap(Location& other)
{
    std::swap(x, other.x);
    std::swap(y, other.y);
}
于 2010-09-13T00:12:53.390 回答
4
Location& Location::operator = (Location other)
{
    if(this == &other)
        return other;
    x = other.x;
    y = other.y;
    return *this;
}

这将返回一个引用。当函数返回时,会发生什么other?(它死了,你什么都没有提到。)因为这是你在问题区域周围处理的类,这可能是原因。重新排列周围的代码会使堆栈处于引用死变量“有效”的特定条件下。

将其更改为return *this,或完全删除检查。(在现代 CPU 上,分配两个没有分支的变量可能总是比添加分支运行得更快。)

(您通常还应该通过引用而不是按值来获取参数。)

于 2010-09-13T00:09:59.657 回答
3

您是否检查过导致此异常的代码?这里引用的Heisenbug现象:

一个常见的例子是在使用优化编译器编译的程序中出现的错误,但在没有优化的情况下编译时不在同一程序中(例如,用于生成调试模式版本)

以下是一些指导方针:

  • 比赛条件?你在使用线程吗?
  • 指针溢出边界某处?
  • 通过valgrind运行代码以监视某处内存缓冲区中的任何异常/不稳定的变化

另一个引用:

出现类似 heisenbug 的行为的一个常见原因是,在调试模式下执行程序通常会在程序启动之前清理内存,并将变量强制到堆栈位置,而不是将它们保存在寄存器中。这些执行上的差异可以改变涉及越界成员访问或对内存初始内容的错误假设的错误的影响。另一个原因是调试器通常会提供监视或其他用户界面,这些界面会导致执行附加代码(例如属性访问器),这反过来又会改变程序的状态。另一个原因是 core 上的 fandango,指针超出范围的影响。在 C++ 中,许多 heisenbugs 是由未初始化的变量引起的。

确保开关已关闭 - 没有优化、完整的调试信息、清除任何现有构建、重新启动 IDE 并再次重新编译......

于 2010-09-12T23:59:11.490 回答
1

首先,您的 Location::operator= 应该是这样的:

Location& Location::operator = (const Location &other)
{
    if(this == &other)
        return *this;
    x = other.x;
    y = other.y;
    return *this;
}

但是,这可能并不能解释崩溃。此处堆栈上的错误指针在大多数体系结构上都不会崩溃(假设 x 和 y 是 int)。

那么,这是曼德尔虫,不是海森虫。你有其他人在某处破坏记忆。祝你好运。

于 2010-09-13T00:16:11.313 回答