У меня есть код С++ (написанный кем-то другим), который, как представляется, вызывает неправильную функцию. Здесь ситуация:
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);
Файлы, для которых выполняется условие true, обрабатываются штрафом; те, для которых это ложная ошибка. Иерархия классов для 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 С++ показывает, что вызов readDocument на BadDocumentReader вызывает не
readDocument(InputStream&, const wchar_t*)
а скорее
readDocument(const LocatedString* source, const wchar_t *, Symbol)
Это подтверждается приложением инструкций cout во всех readDocuments. После вызова исходный аргумент, конечно, полон мусора, что вскоре вызывает крах. У LocationString есть конструктор с несимметричным конструктором из InputStream, но проверка с помощью cout показывает, что он не получает вызов. Любая идея, что могло бы объяснить это?
Изменить: другие, возможно, релевантные детали: классы DocumentReader находятся в другой библиотеке, кроме кода вызова. Я также сделал полную перестройку всего кода, и проблема осталась.
Изменить 2. Я использую Visual С++ 2008.
Изменить 3. Я попытался создать "минимально компилируемый пример" с тем же поведением, но не смог реплицировать проблему.
Изменить 4:
В предложении Billy ONeal я попытался изменить порядок методов readDocument в заголовке BadDocumentReader. Разумеется, когда я меняю порядок, он меняет, какая из функций вызывается. Мне кажется, что я подтверждаю мое подозрение, что что-то странное происходит с индексированием в 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
Я не знаю много сборки, но мне кажется, что это разыменовывает указатель переменной читателя. Первое, что хранится в этой части памяти, должно быть указателем на vtable, поэтому это различие в eax. Затем он помещает первое в vtable в edx и вызывает его. Перекомпиляция с разными порядками методов, похоже, не изменяет этого. Он всегда хочет назвать первое в vtable. (Я мог бы совершенно неправильно понять это, не имея знаний о собрании вообще))
Спасибо за вашу помощь.
Изменить 6: Я нашел проблему, и извиняюсь за то, что тратил все время. Проблема заключалась в том, что GoodDocumentReader должен был быть объявлен как подкласс DocumentReader, но на самом деле этого не было. Стили C-стиля подавляли ошибку компилятора (должны были выслушать вас, @sellibitze, если вы хотите отправить свой комментарий в качестве ответа, я буду отмечать его как правильно). Трудность в том, что код работал в течение нескольких месяцев с чистой случайностью до пересмотра, когда кто-то добавил еще две виртуальные функции в GoodDocumentReader, поэтому он больше не вызывал правильную функцию на удачу.