C++ calling completely wrong (virtual) method of an object

后端 未结 4 2088
鱼传尺愫
鱼传尺愫 2021-02-19 03:43

I have some C++ code (written by someone else) which appears to be calling the wrong function. Here\'s the situation:

UTF8InputStreamFromBuffer* cstream = foo();         


        
相关标签:
4条回答
  • 2021-02-19 04:00

    Based on the assembly, it seems pretty clear that the binding is dynamic and from the first entry of the vtable. The question is which virtual table!?! I would suggest you use a static_cast instead of a C-style cast (of course, @VJo: dynamic_cast is not needed in this case!). There is nothing in the standard that requires a pointer BadDocumentReader* ptr to have the same actual value (address) as its cast static_cast<DocumentReader*>(ptr). This would explain why it binds the call to the first entry of the vtable of BadDocumentReader and not to the vtable of its base class. And, btw, you shouldn't need a cast at all in this case.

    One possibility that doesn't really agree with the asm, but still good to know. Because you create the BadDocumentReader in the same scope as you are calling the reader->readDocument, the compiler gets a little too clever and decides that it can resolve the call without having to look it up in the vtable dynamically. This is because it knows that the "real" type of the reader pointer is actually BadDocumentReader. So it bipasses the vtable and binds the call statically. At least, that is one possibility which I had happen to me in an almost identical situation. Based on the asm, however, I'm pretty sure the first possibility is the one occurring in your case.

    0 讨论(0)
  • 2021-02-19 04:14

    I had this issue and the problem for me was that I was storing it in a class member variable. When I changed it to a pointer and involved new/delete, it successfully registered the child class and its function.

    0 讨论(0)
  • 2021-02-19 04:17

    This is happening because different source files disagree on the vtable layout of the class. The code calling the function thinks that readDocument(InputStream &, const wchar_t *) is at a particular offset, whilst the actual vtable has it at a different offset.

    This usually occurs when you change the vtable, such as by adding or removing a virtual method in that class or any of its parent classes, and then you recompile one source file but not another source file. Then, you get incompatible object files, and when you link them, things go boom.

    To fix this, do a full clean and rebuild of all of your code: both the library code and your code that uses the library. If you don't have the source code to the library but you do have the header files for it with the class definitions, then that is not an option. In that case, you cannot modify the class definition -- you should revert it to how it was given to you and recompile all of your code.

    0 讨论(0)
  • 2021-02-19 04:18

    I would try removing the C-cast first.

    • It is completely unnecessary, casts from a Derived to a Base is natural in the language
    • It may, actually, cause a bug (though it's not supposed to)

    It looks like a compiler bug... it would certainly not be the first either in VS.

    I unfortunately don't have VS 2008 at hand, in gcc the casts occur correctly:

    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
    
    0 讨论(0)
提交回复
热议问题