我有一些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);
满足条件的文件能够正常处理,不满足条件的文件会导致程序崩溃。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);
}
以下内容也与此相关:
class UTF8InputStreamFromBuffer : public wistringstream {
// foo
};
typedef std::basic_istream<wchar_t> InputStream;
在Visual C++调试器中运行时,它显示对BadDocumentReader的readDocument调用不是有效的。readDocument(InputStream&, const wchar_t*)
但是更确切地说
readDocument(const LocatedString* source, const wchar_t *, Symbol)
这一点通过在所有readDocuments中插入cout语句来确认。 在调用后,源参数当然充满了垃圾,这很快导致崩溃。 LocatedString确实有一个从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
我不太了解汇编语言,但看起来它正在取消引用reader变量指针。存储在此内存部分中的第一件事应该是指向vtable的指针,因此它将其取消引用为eax。然后它将vtable中的第一项放入edx并调用它。重新编译不同顺序的方法似乎不会改变这一点。它总是想调用vtable中的第一项。(我可能完全误解了这一点,因为我根本不懂汇编语言...)感谢您的帮助。
编辑6:我找到了问题,并为浪费大家的时间道歉。问题在于GoodDocumentReader应该被声明为DocumentReader的子类,但实际上并没有。C风格的转换抑制了编译器错误(如果您想将您的评论作为答案提交,我会将其标记为正确答案)。棘手的是,代码已经通过纯粹的意外运行了几个月,直到有人添加了两个更多的虚函数到GoodDocumentReader,所以它不再通过运气调用正确的函数。
wistringstream
继承。如果你想要使用UTF-8编码,你应该使用codecvt facet
来完成;毕竟,这正是codecvt facet
的设计目的。boost库有一个相关的实现,而且我相信MSVC++也内置了这样的功能。 - Billy ONeal