Reputation: 279
I came across some dodgy code recently. But the behavior left me a bit dumbfounded. The following is a simplification of the problem (basically the virtual keyword is missing, making it non-polymorphic). Why/how is it printing "C::foo called, i: 5"?
How is the object in memory even able to have 'i' in it? I tried C++03 and C++11.
#include <iostream>
using namespace std;
class P
{
public:
void foo()
{
cout << "P::foo called" << endl;
}
};
class C : public P
{
public:
void foo()
{
i = 5;
cout << "C::foo called, i: " << i << endl;
}
int i;
};
int main()
{
C* c = static_cast<C*>(new P());
c->foo();
}
Upvotes: 0
Views: 172
Reputation: 1266
"Why/how is it printing "C::foo called, i: 5"?"
In static_casting the P pointer to a C pointer you're saying to the complier interpret this bit of memory as if it contained an object of class C
So when you call foo on that it looks up Cs version of foo.
(That explains the C:foo called... but not how i is 5)
"How is the object in memory even able to have 'i' in it"
In a way it doesn't. Having been told that this memory contains a C, the complier then "knows" that i lives at a certain memory offset from the objects this pointer. That memory hasn't been allocated especially to contain an i member for an object of type C (no objects of C have been created) but having been told (wrongly) that the memory does contain a C, it'll use that address as i, and, because you set it in foo before you output it, that misappropriated address will end up containing 5.
If you don't set i in foo, but you initialise it when its declared instead, you'd see that a real C object outputs the value you set it to, but that a P* cast as a C* outputs whatever happens to be at that address.
Upvotes: 1
Reputation: 279
After much research the conclusion: "undefined behaviour"
Here is the rule for downcasting using static_cast
, found in section 5.2.9 ([expr.static.cast]
) of the C++ standard (C++0x wording):
A prvalue of type "pointer to cv1
B
", whereB
is a class type, can be converted to a prvalue of type "pointer to cv2D
", whereD
is a class derived fromB
, if a valid standard conversion from "pointer toD
" to "pointer toB
" exists, cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, andB
is neither a virtual base class ofD
nor a base class of a virtual base class ofD
. The null pointer value is converted to the null pointer value of the destination type. If the prvalue of type "pointer to cv1B
" points to aB
that is actually a subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. Otherwise, the result of the cast is undefined.
ref:- Downcasting using the 'static_cast' in C++
Upvotes: 0
Reputation: 941
That's exactly the expected behaviour, nothing strange with it. "Notes static_cast may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type"
http://en.cppreference.com/w/cpp/language/static_cast
I'm not able yo verify right now, but in an instance of a child object you should be able to specify which of the two "foo" you want to call by specifying the namespace. Not sure about that when a static cast occurs though.
void C::foo()
{
if (/*something*/)
// do something
else
D::foo();
}
Upvotes: 0