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