잘못된 함수를 호출하는 것으로 보이는 일부 C++ 코드 (다른 사람이 작성한 코드)가 있습니다. 상황은 다음과 같습니다.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);
조건이 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;
비주얼 C에서 실행 ++ 디버거, 그것은 BadDocumentReader에 readDocument 호출하지
를 호출하는 것을 보여줍니다readDocument(InputStream&, const wchar_t*)
아니라
readDocument(const LocatedString* source, const wchar_t *, Symbol)
이것은 모든 readDocuments에 cout 문을 고수함으로써 확인됩니다. 콜 후 소스 인자는 물론 쓰레기로 가득 차서 곧 충돌을 일으킨다. LocatedString은 InputStream에서 하나의 인자를 갖는 암시 적 생성자를 가졌지 만, cout을 검사하면 호출되지 않는 것으로 나타납니다. 어떤 아이디어가 이것을 설명 할 수 있습니까?
: 다른 가능한 관련 세부 정보 : DocumentReader 클래스가 호출 코드와 다른 라이브러리에 있습니다. 또한 모든 코드를 완전히 다시 작성 했으므로 문제가 남아 있습니다.
편집 2 : 나는 비주얼 C++ 2008
편집 3을 사용하고 있습니다 : 나도 같은 행동은 "최소한의 컴파일 가능한 예"를 제작하려고했으나 문제를 복제 할 수 없습니다.
편집 4는 : 빌리 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
나는 많은 어셈블리를 모르겠지만, 독자 변수 포인터를 역 참조 것처럼 그것은 나에게 보인다. 메모리의이 부분에 저장된 첫 번째 것은 vtable에 대한 포인터 여야하므로 eax로 다시 참조됩니다. 그런 다음 을 먼저 것으로 edx의 vtable에 넣고 호출합니다. 서로 다른 순서의 메소드로 재 컴파일하면 이것을 변경하지 않는 것 같습니다. 항상 vtable에서 첫 번째 것을 호출하려고합니다. (나는 이것에 대해 완전히 오해 할 수 있었고, 조립에 대한 지식이 전혀 없었을 것입니다 ...)
당신의 도움에 감사드립니다.
편집 6 : 편집 : 문제가 발견되어 모든 사람의 시간을 낭비하는 것에 사과드립니다.문제는 GoodDocumentReader가 DocumentReader의 서브 클래스로 선언되었지만 실제로는 그렇지 않다는 것이 었습니다. C 스타일의 캐스트는 컴파일러 오류를 억제하고있었습니다 (@sellibitze를 들었어 야합니다. 답글로 의견을 제출하려면 올바른 것으로 표시 할 것입니다). 까다로운 점은 누군가가 GoodDocumentReader에 두 개의 가상 함수를 추가하여 더 이상 올바른 함수를 호출하지 않을 때까지 코드가 몇 달 동안 순수 사고로 작동했기 때문이라는 것입니다.
'DefaultDocumentReader'란 무엇입니까? –
문제를 보여주는 가능한 최소화 가능한 예제로 잘라내시겠습니까? –
올리 : 죄송합니다. "BadDocumentReader"여야합니다. 게시물을 업데이트했습니다. –