我在这里遇到了一个奇怪的问题。假设我有一个带有一些虚拟方法的类。在某种情况下,此类的实例应调用其中一种方法。大多数情况下,该阶段没有问题,但有时会发现无法调用虚方法,因为指向该方法的指针为NULL(如VS所示),因此发生内存访问冲突异常。怎么会发生这种情况?
应用程序非常庞大和复杂,所以我真的不知道低级步骤会导致这种情况。发布原始代码没有用处。
UPD:好的,我看到我对这个问题的介绍是不确定的,所以示意代码看起来像
void MyClass::FirstMethod() const { /* Do stuff */ }
void MyClass::SecondMethod() const
{
// This is where exception occurs,
// description of this method during runtime in VS looks like 0x000000
FirstMethod();
}
不涉及构造函数或析构函数。
答案 0 :(得分:5)
堆腐败可能是候选人。对象中的v表指针易受攻击,它通常是对象中的第一个字段。对于碰巧与对象相邻的某种其他对象的缓冲区溢出将擦除v表指针。通常很久以后,对虚拟方法的调用将会受到打击。
另一个经典案例是有一个糟糕的“this”指针,通常是NULL或低值。当您调用方法的对象引用不好时,就会发生这种情况。该方法将照常运行,但一旦尝试访问类成员就会爆炸。再次,堆损坏或使用已删除的指针将导致此问题。祝你好好调试一下;这绝非易事。
答案 1 :(得分:2)
可能你正在从一个本身没有该函数的基类的构造函数中直接或间接地调用该函数。
可能在某处有一个破坏的强制转换(例如当涉及多个继承时指针的reinterpret_cast
)并且你正在查看错误类的vtable。
可能(但不太可能)你以某种方式破坏了vtable。
对于此对象或同一类型的所有其他对象,指向函数的指针是否为null?如果是前者,则vtable指针被破坏,而你正在寻找错误的地方。如果是后者,那么vtable本身就会被打破。
答案 2 :(得分:1)
如果您尝试在析构函数或构造函数中调用纯虚方法,则可能发生这种情况。此时,可能无法初始化该方法的虚拟表指针,从而导致崩溃。
答案 3 :(得分:1)
在SecondMethod的处理过程中,“this”指针是否可能被删除?
另一种可能性是,实际上正在使用前面的无效指针调用SecondMethod,并且它恰好工作(通过未定义的行为)直到嵌套的函数调用,然后失败。如果您能够添加打印代码,请在执行这些方法时检查“this”和/或其他指针是否在各个点上使用0xcdcdcdcd或0xfdfdfdfd。这些值是(我相信)VS在内存alloc / dealloc上使用的,这可能是它在调试模式下编译时的原因。
答案 4 :(得分:0)
您最有可能看到的是实际问题的副作用。很可能是堆或内存损坏,或引用以前释放的对象或空指针。
如果你可以在同一个地方一直让它崩溃并且可以找出从那里加载空指针的位置那么我建议使用调试器并在该内存位置放置一个断点,一旦断点被触发那么你很可能正在查看实际导致腐败的代码。
答案 5 :(得分:0)
如果仅在Studio无法显示方法地址时发生内存访问冲突,则可能是由于缺少调试信息。您可能正在调试使用release(非调试)编译器/链接器标志编译的代码。
尝试在项目的C ++属性中启用一些调试信息,重建并重新启动调试器。如果它会有所帮助,您将看到所有正常的可追踪事物,如堆栈,变量等。
答案 6 :(得分:0)
如果您的this指针为NULL,则不太可能发生损坏。除非你把记忆归零,否则你不应该这样做。
您没有说您是在调试Debug(未优化)还是Release(优化)版本。通常,在Release构建优化器中将删除此指针(如果不需要)。因此,如果您正在调试优化版本,将此指针视为0并不意味着什么。你必须依靠反汇编来告诉你发生了什么。如果无法在Debug构建中重现问题,请尝试关闭Release构建中的优化。调试优化版本时,您正在调试程序集而不是C ++。
如果您已经在调试非优化版本,请确保在花费太多时间调试损坏的图像之前进行了干净的重建。调试版本通常以递增方式链接,并且已知增量链接器会产生这样的问题。如果你正在使用干净的构建运行Debug构建,但仍然无法弄清楚出了什么问题,请发布堆栈转储和更多代码。我相信我们可以帮你弄清楚。