Sak
Sak

Reputation: 279

Unexpected C++ behaviour in the following program

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

Answers (3)

ROX
ROX

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

Sak
Sak

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", where B is a class type, can be converted to a prvalue of type "pointer to cv2 D", where D is a class derived from B, if a valid standard conversion from "pointer to D" to "pointer to B" exists, cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The null pointer value is converted to the null pointer value of the destination type. If the prvalue of type "pointer to cv1 B" points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

ref:- Downcasting using the 'static_cast' in C++

Upvotes: 0

Barnack
Barnack

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

Related Questions