0

Here's a piece of code I had written to see the behaviour during downcasting.

#include <iostream>
using namespace std;

class base {
public :
    void function()
    {
        cout << "\nInside class Base";
    }
};

class derived : public base {
public :
    void function()
    {
        cout << "\nInside class Derived.";
    }
};

int main()
{
    base * b1 = new base();
    base * b2 = new derived();
    derived * b3 = (derived*)b1 ;
    b1 -> function();
    b2 -> function();
    b3 -> function(); // print statement 3
    static_cast<derived*>(b2) -> function();
    static_cast<derived*>(b1) -> function(); // print statement 5
    return 0;
}

The output is as follows .

Inside class Base
Inside class Base
Inside class Derived.
Inside class Derived.
Inside class Derived.

I feel print statement 3 and print statement 5 should have displayed "Inside class base" .

Can someone please explain what I might be missing here?

1
  • You want virtual functions. Normally, the function is chosen based on the static (compile time) type it is being called on (i.e. you call it on a derived*, you get the derived version). If you make the function virtual, you get the version for the "actual" (or dynamic) type, as you want. e.g. see here or here. Commented Jul 26, 2013 at 13:01

4 Answers 4

5

Both are cases of undefined behavior. Casting b1 to derived* is not valid.

However, if you said base* b1 = new derived(), you would have the same behavior. Since neither function is marked virtual, then it only checks objects type at compile time.

So the first case would print "Inside class Base", even though its actually a derived pointer.

Sign up to request clarification or add additional context in comments.

6 Comments

It looks like both 3 and 5 are UB. They are almost identical.
Not sure about this, but I have an inkling it is ok because they are standard layout classes (empty in fact, although that doesn't mean sizeof(base)==0 or for derived). I.e. you are allowed nasty casts in some cases to be able to do what everyone does in c anyway.
@BoBTFish: While it works in this case, mostly for the reasons you state, it doesn't have to: 5.2.9p11 ... 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.
Took me a while to dig through The Standard for the right terminology, but I shall just direct you to this, and mention the phrase "layout-compatible types". (Although there it mentions reinterpret_cast, rather than static_cast.)
@BoBTFish: Ah, I wasn't aware they said that reinterpret_cast of two layout-compatible types was valid. So it's not necessarily as ill-formed as at first glance.
|
1

You need to define base method void function() as virtual:

virtual void function()
{
    cout << "\nInside class Base";
}

and resulting output is:

Inside class Base
Inside class Derived.
Inside class Base
Inside class Derived.
Inside class Base

In OP, 5th case may not be an undefined behaviour as stated in the reference1 and inline member function memory is not stored like data members as stated here insuring that after static cast to derived type, derived member function is called:

The inverse of any standard conversion sequence (Clause 4) not containing an lvalue-to-rvalue (4.1), array-to- pointer (4.2), function-to-pointer (4.3), null pointer (4.10), null member pointer (4.11), or boolean (4.12) conversion, can be performed explicitly using static_cast.

1 Working Draft, Standard for Programming Language C++, 5.2.9 Static Cast - 7

1 Comment

I understand that if you declare it as virtual , then the above output can be seen . But my doubt is that the derived class function should not be there in the memory layout of b1 at all . So how can a function be called which is not present in the memory?
0

The functions are dispatched at compilation time, based solely on the static type, as they are not virtual.

Static type of b3 in print statement is Derived *, hence 'Inside class Derived'. Print statement 5 casts Base * to Derived *, hence the same printout.

Add virtual to function() definition in Base and check again to see what happens.

1 Comment

But b1 should contain only the function which displays "Inside class base" since it points to a object of type base . The derived class function should not be there at all in the memory layout of b1 . So how can it be possibly pointed to?
-1

That's the expected behavior.
Non virtual methods are called by the object's type at compile time.
You used a static cast, so the compiler treats it as "Derived" class.

If you'd declare the method as virtual then it will create a virtual look up table for the function, and call the method according to the actual run-time type.

3 Comments

Personally, I expected it to print a purple unicorn, or whatever else it feels like: expected undefined behavior is rathrr misleading, even if what you describe is in line with many implementations.
@Yakk Only line 3 is not well defined. But what it really does is that the compiler will treat the object as the object you said it is (just like reinterpert_cast). Because the objects are related the compiler will add the right offset to get to the base of the derived class. Then it will call the derived class's function. Because there are no members to either class that offset will be 0.
@Yakk just because it's not well defined in the C++ standard doesn't mean it's not an expected behavior if you know how the compiler works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.