RamBo
RamBo

Reputation: 35

Calling Derived class function from a Base class pointer after typecasting it to Derived class pointer

I am fairly new to C++ (& OOP). I am struggling to understand the following piece of code:

#include <iostream>

class Base {
public:
    Base() {
        std::cout << "In Base Constr: " << __FUNCSIG__ << std::endl;
    }

    virtual ~Base() {
        std::cout << "In Base Destr: " << __FUNCSIG__ << std::endl;
    }

    void A() {
        std::cout << "In Base func A " << __FUNCSIG__ << std::endl;
    }
};

class Derived : public Base {
    public:
    Derived() {
        std::cout << "In Derived Constr: " << __FUNCSIG__ << std::endl;
    }

    ~Derived() {
        std::cout << "In Derived Destr: " << __FUNCSIG__ << std::endl;
    }

    void B() {
        std::cout << "In Derived func B " << __FUNCSIG__ << std::endl;
    }
};

void test(Base* b) {
    Derived* d = static_cast<Derived*>(b);
    d->A();
    d->B();              // How is this valid??
}

int main() {
    Base *b = new Derived();
    std::cout << "In main" << std::endl;
    b->A();
    std::cout << __LINE__ << std::endl;

    Base *bb = new Base();
    std::cout << __LINE__ << std::endl;
    test(bb);

    delete b;
    delete bb;
}

I am not sure, why & how the line d->B() works? even though the pointer was typecasted to Derived class, but the Base class object itself should not have that function in memory.

Upvotes: 0

Views: 643

Answers (3)

super
super

Reputation: 12928

It is not valid. It is Undefined Behavior.

The thing is that the compiler allows you to write such code. Casting from a Base* to a Derived* would be valid if the object pointed to was actually a Derived. It's up to you as the programmer to make sure it's valid.

There are many situations in C++ where you can shoot yourself in the foot like this. It's part of the language.

Out of bounds access, dereferencing dangling pointers/references and invalid casts to name a few of the most common ones.

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385144

I am not sure, why & how the line b->B() works? [..] the Base class object itself should not have that function in memory

You're right! It doesn't work!

(Well, functions aren't stored "in memory", but…)

The call is invalid. The static_cast says "I promise that this Base* points to a Derived". That promise was broken.

The program has undefined behaviour. That can in practice mean that things "appear" to work, especially if no member variables were touched by the non-existent function...

Upvotes: 3

aschepler
aschepler

Reputation: 72311

It is undefined behavior to static_cast to a derived class when the object is not actually of that derived type. But undefined behavior means anything could happen, including seeming to work. (Or seeming to work today, then failing later at the worst possible time.)

So that's all there is for an official explanation from the point of view of the C++ language.

But as for why this probably does work for a typical real compiler and computer: The code for a member function isn't actually stored inside the objects, since that would be a lot of bytes. For a non-virtual function, there isn't even any pointer or such thing inside the object to the function. Instead, the compiler will implement the function Derived::B essentially like a non-member function:

void __mangled_Derived_B(Derived const* this) { /*...*/ }

Then every time the B function is called, it will just pass the correct pointer to become the "this" argument.

In your example, Derived::B doesn't actually use this at all, not even implicitly, so issues are unlikely. But if it attempted to use a data member of Derived, things would get much more risky, possibly causing strange results, changes to other objects, or crashes.

Upvotes: 2

Related Questions