【问题标题】:Access violation in call to non-virtual member function调用非虚拟成员函数时的访问冲突
【发布时间】:2012-01-27 09:47:41
【问题描述】:

我有一段代码运行了很多次,突然报告访问冲突。

for(std::list<Projectile>::iterator it = projectiles.begin(); it != projectiles.end();) {
    bool finished = ...

    // try to print address of collides(), prints always 1
    std::cout << &Projectile::collides << std::endl; 
    // here it crashes:
    if(it->collides(&hero)) { 
        std::cout << "Projectile hits " << hero.getName() << std::endl;
        finished = it->apply(&hero);
    }

    // ...
    if(finished) {
        it = projectiles.erase(it);
    } else {
        ++it;
    }
}

所以 VS 调试堆栈跟踪表明,在行 if(it-&gt;collides(&amp;hero)) { 程序尝试调用 cdcdcdcd() 的方法,这会导致访问冲突。

it*ithero 根据 VS 是有效对象。

所以我假设cdcdcdcd() 实际上应该是collides()。由于碰撞是一种非虚拟方法,它的地址应该基本上不会改变或者?

问题是方法collides()在成功之前执行了几次,但突然它不再起作用了。

会不会是地址变了?我覆盖了吗?

请帮帮我!另外,我感谢有关此代码不合适的任何信息的信息:)

【问题讨论】:

  • 我认为您发布的 sn-p 太小了。这段代码似乎没有任何明显错误,真正的问题可能出在其他地方。你能创建一个SSCCE吗?
  • 经常出现这样的问题是因为缺少锁定。对这个集合的任何访问都应该被锁定,否则一个线程可能会清除它,而另一个线程正在迭代。最后,症状很难重现,因为这是一个“时间问题”。

标签: c++ access-violation


【解决方案1】:

0xcdcdcdcd 是 Win32 调试 CRT 堆使用的填充模式;假设您在 Windows 上的调试器中运行,这可能是一个重要的线索。有一个不错的explanation of the heap fill patterns here

我的猜测是你要么在其他地方使迭代器无效,要么有其他缓冲区溢出或悬空指针或其他问题。

Application Verifier 可能有助于诊断。您可能还想查看问题How to debug heap corruption errors? 中提到的其他内容或Any reason to overload global new and delete? 中的一些技术(免责声明:目前我对这两个问题都有最高评价)。

如果您的 STL 库具有调试功能,它也可能有助于找出这一点。 Metrowerks(现为飞思卡尔)Codewarrior 具有定义 _MSL_DEBUG,例如,可用于构建标准库版本(包括 std::list),它将检测迭代器失效等常见问题(以一定的运行时成本)。

看起来 Visual Studio 有 debug iterator support,如果你正在使用它,它可能符合要求。

【讨论】:

  • 这个错误现在已经消失了......我不知道我是否做了什么来修复它。我什至不知道我是否修复了它。不过,您的回答非常有帮助。我想我必须让我的代码更可靠,更不容易出现内存错误。
【解决方案2】:

你的终止条件似乎错误[编辑:实际上它看起来是正确的,即使它仍然让我害怕。见 cmets。]。当finished 变为真时,你会擦除当前的弹丸,然后将迭代器设置为 ... 一些东西。没有理由认为循环将在此时终止。 it 很可能最终会开始指向列表之外,或者指向其他无效的对象(您的调试器不会总是这样标记)。

当你擦除弹丸时,你应该明确地使用“break”离开循环。

【讨论】:

  • afaik erase() 将迭代器返回到列表中删除后的下一项。如果删除的项目是列表中的最后一个,则返回 end()-iterator。还是我错了?
  • @user1104631:这是正确的:“返回值是紧跟被擦除元素的迭代器。” sgi.com/tech/stl/Sequence.html
  • 好的,我收回。最后的条件是正确的。虽然我仍然认为他应该使用明确的休息,但我不再有确切的理由。
  • 原因很明显。循环要么完成,要么没有。继续迭代就是自找麻烦,这正是 OP 得到的。如果erase 返回的迭代器不在列表的末尾,那么循环可能会擦除更多的元素,这可能是出乎意料的。如果这种行为预期的,那么finished 是一个极差的变量名选择。
猜你喜欢
  • 2014-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
  • 2022-11-03
  • 1970-01-01
  • 2013-05-24
相关资源
最近更新 更多