3

我的一位同事今天遇到了一些 C++ 代码的问题。他正在调试对象虚方法的怪异行为。每当执行该方法时(在调试下,Visual Studio 2005 下),一切都出错了,调试器不会进入该方法,而是进入对象的析构函数!另外,对象的虚拟表,只列出了它的析构函数,没有其他方法。

我以前从未见过这种行为,并且打印了运行时错误,说明了有关ESP寄存器的内容。我希望我能给你正确的错误信息,但我现在记不正确了。

无论如何,你们有没有遇到过这种情况?什么可能导致这种行为?那将如何解决?我们多次尝试重建项目,重新启动 IDE,没有任何帮助。我们还在_CrtCheckMemory该方法调用之前使用该函数来确保内存处于良好状态,并且它返回true(这意味着 ok )。我没有更多的想法了。你?

4

5 回答 5

2

我以前见过。通常发生这种情况是因为我在调试模式下使用的是 Release 构建的 .LIB 文件中的类。其他人可能已经看到了一个更好的例子,我会对他们的回答做出我的回答。

于 2010-01-07T21:33:32.550 回答
1

也许你在需要 a 的地方使用 C 风格的演员表static_cast<>?每当涉及多重继承时,这可能会导致您报告的那种错误,例如:

class Base1 {};
class Base2 {};
class Derived : public Base1, public Base2 {};

Derived *d = new Derived;
Base2* b2_1 = (Base2*)d; // wrong!
Base2* b2_2 = static_cast<Base2*>(d); // correct
assert( b2_1 == b2_2 ); // assertion may fail, because b2_1 != b2_2

请注意,情况可能并非总是如此,这取决于编译器和所涉及的所有类的声明(当所有类都具有虚拟方法时,可能会发生这种情况,但我手头没有确切的规则)。

或者:您的代码的一个完全不同的部分正在变得疯狂并且正在覆盖内存。尝试隔离错误并检查它是否仍然发生。CrtCheckMemory只会发现少数情况会覆盖内存(例如,当您写入特别标记的堆管理位置时)。

于 2010-01-07T22:02:14.693 回答
1

如果您曾经使用错误数量的参数调用函数,这很容易最终破坏您的堆栈并产生未定义的行为。我似乎记得使用 MFC 时的某些错误很容易导致这种情况,例如,如果您使用调度宏将消息指向一个没有正确数量或类型参数的方法(我似乎记得那些函数没有严格检查指针的类型)。自从我上次遇到这个特殊问题以来可能已经有十年了,所以我的记忆很模糊。

于 2010-01-07T22:09:13.230 回答
1

ESP 的值未在函数调用中正确保存。

这种行为通常表明调用代码已使用与创建相关特定类的代码不同的类或函数定义进行编译。

是否有可能正在加载不同版本的组件 dll 而不是新构建的?如果您将内容复制为构建后步骤的一部分,或者如果该进程从不同的目录运行或在执行LoadLibrary或等效操作之前更改其 dll 搜索路径,则可能会发生这种情况。

我经常在复杂的项目中遇到它,其中更改了类定义以添加、删除或更改虚函数的签名,然后完成增量构建,实际上并不是所有需要重新编译的代码都被重新编译。从理论上讲,如果程序的某些部分覆盖了某些多态对象的 vptr 或 vtables,则可能会发生这种情况,但我总是发现错误的部分构建更可能是原因。

这可能是“用户错误”,开发人员故意告诉编译器在应该重建其他项目时只构建一个项目,或者它可能有多个解决方案或解决方案中的多个项目,其中依赖项设置不正确。

即使解决方案中的项目正确链接,Visual Studio 有时也会出现问题,并且无法正确生成生成的依赖项。这种情况发生的频率低于 Visual Studios 的责任。

删除所有中间构建文件并从源代码重建所有内容通常可以解决问题。显然,对于非常大的项目,这可能是一个严重的惩罚。

于 2010-01-07T23:04:01.527 回答
1

因为无论如何它都是猜测,这是我的一个:

你的堆栈搞砸了,_CrtCheckMemory没有检查。至于堆栈损坏的原因:

  • 好旧的堆栈溢出
  • 调用约定不匹配,已经提到过(我不知道,比如将错误调用约定中的回调传递给 WinAPI 函数;你链接的是什么静态或动态库?)
  • 一条线printf("%d");
于 2010-01-08T00:59:07.210 回答