Explain bug behaviour
Can you please explain what's going on in this buggy example:
Base base; Derived* d = reinterpret_cast<Derived*> (&base);
d->method();
d->virtual_method();
//output: Derived-method() Base-virtual_开发者_如何学Cmethod()
I would expect this code to behave the other way around. Probably the compiler shares a single memory layout for Base and Derived, and of course the vtable is common.
- When d->method is invoked I would expect the compiler to say "I'm just calling method at offset 0 with respect to my pointer. The pointer points to Base object, the only object around.
- When d->virtual_method is invoked the compiler should say I am going to resolve it through the vtable, and thus the Derived method should be called (though the Base object is the only one around, the layout extends to Derived).
So I am expecting to see:
//output: Base-method() Derived-virtual_method()
base
is a Base
object; you reinterpret its bytes as a Derived
object and then attempt to use it as if it were a Derived
object. The behavior when you do this is undefined. Your program might crash; it might appear to do the right thing; it might make your computer light on fire.
Note that it is never correct to use reinterpret_cast
to cast up and down a class hierarchy. You must use static_cast
or dynamic_cast
(or, if you are converting to a base class, no cast may be necessary).
To explain why you see this particular behavior, though: when you call a nonvirtual member function (as you do with d->method()
, assuming method
is a nonvirtual member function of Derived
), the function that gets called is determined at compile time, not at runtime.
Here, the compiler knows that d
points to a D
object (because you've lied to the compiler and said that it is), so it generates code that calls Derived::method()
. There is no "offset with respect to a pointer" at all. No computation needs to be done because the function to be called is known when the program is compiled.
Only when you call a virtual member function is a table lookup required (and even then, the lookup is only required when the compiler doesn't know the dynamic type of the object on which the member function is being called).
When you call d->virtual_method()
, Base::virtual_method
gets called. Why? In this particular implementation of C++, the first few bytes of an object of a class type that has virtual member functions (a polymorphic class type) contain a tag (called a "vptr" or a "virtual table pointer") that identifies the actual type of the object. When you call a virtual member function, then at runtime that tag is inspected and the function that is called is selected based on that tag.
When you reinterpret base
as a Derived
object, you don't actually change the object itself, so its tag still states that it is a Base
object, hence why Base::virtual_method
gets called.
Remember, though, that all this just happens to be what happens when you compile this code with a particular version of a particular compiler. The behavior is undefined and this is just one way that the undefined behavior can manifest itself.
The compiler only allocates enough memory to hold the requested object. Base might be 20 bytes, and Derived might be an extra 10 bytes on top of that (so Derived is 30 bytes in size.)
When you allocate 20 bytes for Base, and then (via Derived) access byte position 25, it's past the end of the allocated memory and (at best) you will get a crash.
The compiler cannot allocate 30 bytes for Base as you suggest, because not only would this be wasteful, but Derived could be implemented in a third party library and it may not even be known about when Base is being compiled.
精彩评论