Rya*_*ard 15 c++ virtual-functions overloading
我有一些C++代码(由其他人编写)似乎调用了错误的函数.情况如下:
UTF8InputStreamFromBuffer* cstream = foo();
wstring fn = L"foo";
DocumentReader* reader;
if (a_condition_true_for_some_files_false_for_others) {
reader = (DocumentReader*) _new GoodDocumentReader();
} else {
reader = (DocumentReader*) _new BadDocumentReader();
}
// the crash happens inside the following call
// when a BadDocumentReader is used
doc = reader->readDocument(*cstream, fn);
Run Code Online (Sandbox Code Playgroud)
条件为真的文件处理正常; 它是虚假崩溃的那些.DocumentReader的类层次结构如下所示:
class GenericDocumentReader {
virtual Document* readDocument(InputStream &strm, const wchar_t * filename) = 0;
}
class DocumentReader : public GenericDocumentReader {
virtual Document* readDocument(InputStream &strm, const wchar_t * filename) {
// some stuff
}
};
class GoodDocumentReader : public DocumentReader {
Document* readDocument(InputStream & strm, const wchar_t * filename);
}
class BadDocumentReader : public DocumentReader {
virtual Document* readDocument(InputStream &stream, const wchar_t * filename);
virtual Document* readDocument(const LocatedString *source, const wchar_t * filename);
virtual Document* readDocument(const LocatedString *source, const wchar_t * filename, Symbol inputType);
}
Run Code Online (Sandbox Code Playgroud)
以下也是相关的:
class UTF8InputStreamFromBuffer : public wistringstream {
// foo
};
typedef std::basic_istream<wchar_t> InputStream;
Run Code Online (Sandbox Code Playgroud)
在Visual C++调试器中运行,它显示BadDocumentReader上的readDocument调用没有调用
readDocument(InputStream&, const wchar_t*)
Run Code Online (Sandbox Code Playgroud)
反而
readDocument(const LocatedString* source, const wchar_t *, Symbol)
Run Code Online (Sandbox Code Playgroud)
通过在所有readDocuments中粘贴cout语句来确认这一点.在调用之后,source参数当然充满了垃圾,这很快就会导致崩溃.LocationString确实有一个来自InputStream的单参数隐式构造函数,但是用cout检查表明它没有被调用.有什么可以解释这个吗?
编辑:其他可能相关的细节:DocumentReader类与调用代码位于不同的库中.我还完成了所有代码的完整重建,问题仍然存在.
编辑2:我正在使用Visual C++ 2008.
编辑3:我尝试使用相同的行为制作"最低限度可编辑的示例",但无法复制问题.
编辑4:
在Billy ONeal的建议中,我尝试更改BadDocumentReader标头中readDocument方法的顺序.果然,当我改变顺序时,它会改变调用哪些函数.在我看来,这证实了我的怀疑,因为索引到vtable有一些奇怪的事情,但我不确定是什么导致它.
编辑5:这是函数调用前几行的反汇编:
00559728 mov edx,dword ptr [reader]
0055972E mov eax,dword ptr [edx]
00559730 mov ecx,dword ptr [reader]
00559736 mov edx,dword ptr [eax]
00559738 call edx
Run Code Online (Sandbox Code Playgroud)
我不太了解汇编,但它看起来像是它取消引用读者变量指针.存储在这部分内存中的第一件事应该是指向vtable的指针,因此它将其解释为eax.然后它将第一个东西放在edx中的vtable中并调用它.使用不同的方法顺序重新编译似乎并没有改变这一点.它总是想要在vtable中调用第一件事.(我完全可能误解了这一点,根本不了解装配......)
谢谢你的帮助.
编辑6:我发现了问题,我为浪费每个人的时间而道歉.问题是GoodDocumentReader应该被声明为DocumentReader的子类,但事实上并非如此.C风格的演员阵容抑制了编译错误(应该听你的,@ sellibitze,如果你想提交你的评论作为答案,我会把它标记为正确).棘手的是,代码已经运行了几个月纯粹的意外,直到有人向GoodDocumentReader添加了两个虚函数,因此不再通过运气调用正确的函数.
Ada*_*eld 13
发生这种情况是因为不同的源文件在类的vtable布局上不一致.调用函数的代码认为readDocument(InputStream &, const wchar_t *)是在特定的偏移量,而实际的vtable具有不同的偏移量.
这通常发生在更改vtable时,例如通过在该类或其任何父类中添加或删除虚方法,然后重新编译一个源文件而不是另一个源文件.然后,您将获得不兼容的目标文件,当您链接它们时,事情就会变得繁荣.
要解决此问题,请完全清理并重建所有代码:库代码和使用库的代码.如果您没有库的源代码,但是您确实拥有类定义的头文件,那么这不是一个选项.在这种情况下,您无法修改类定义 - 您应该将其恢复为给定的方式并重新编译所有代码.
我会先尝试删除 C 型铸件。
它看起来像是一个编译器错误...它肯定不是 VS 中的第一个错误。
不幸的是,我手头没有 VS 2008,在 gcc 中,强制转换正确发生:
struct Base1
{
virtual void foo() {}
};
struct Base2
{
virtual void bar() {}
};
struct Derived: Base1, Base2
{
};
int main(int argc, char* argv[])
{
Derived d;
Base1* b1 = (Base1*) &d;
Base2* b2 = (Base2*) &d;
std::cout << "Derived: " << &d << ", Base1: " << b1
<< ", Base2: " << b2 << "\n";
return 0;
}
> Derived: 0x7ffff1377e00, Base1: 0x7ffff1377e00, Base2: 0x7ffff1377e08
Run Code Online (Sandbox Code Playgroud)