开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜