picklepick
picklepick

Reputation: 1607

Calling member of derived class from Base reference object

I found many similar questions on SO, but their solution is either not working or it's slightly different (often using pointers instead, I do not want to change the caller() signature!). If this has already been asked (and answered) please let me know and I'll delete this question.

I found something called the visitor pattern but I'm curious if there is a simpler solution. I try do do it without using virtual since my course did not cover that yet. Or do I need it here?

I have the following code:

class Base
{
public:
    void fun(); // cout << "Base"
};

class Derived : public Base
{
public:
    void fun(); // cout << "Derived"
};

void caller(Base &obj)   // must stay as it is
{
    // here i want to call Derived::fun()
    // obj.fun() prints 'Base' but I need 'Derived'
}

int main()
{
    // Base base;
    // caller(base);

    // EDIT:
    Derived derived;
    caller(derived);

}

What would be the easiest way to have caller() call Derived::fun() instead of Base::fun() when passing a Base & to the function caller()?

If there's a simple answer pointing me in the right direction would be highly appreciated, I've been stuck on this for a while now:)

I know that this is not something you would do in production code, this is about learning C++

EDIT: I changed the caller function argument to type Derived instead of Base

Upvotes: 1

Views: 1304

Answers (2)

You can't. caller is given a reference to an actual Base object. There is no way in C++ to convert this to a reference to a Derived object (because the object actually is not a Derived object). Any attempt to do the conversion will fall foul of Undefined Behaviour. This means that anything may happen. Specifically, the call may appear to work - except when you come to do the demonstration to a major customer at which point it formats your hard disk instead. (This is a slight exaggeration for rhetorical purposes; but undefined behaviour has turned a for loop running up to 64 into an infinite loop).


If we change main slightly, then this becomes a much more interesting question:

int main()
{
    Derived derived;
    caller(derived);

}

Now we can arrange to call Derived::fun. The best solution is to make fun a virtual function. Then it just works:

class Base
{
public:
    virtual void fun() { cout << "Base"; }
};

The call to obj.fun will be directed to Derived::fun (like you wanted), and the code will print "derived".

That's great - and definitely should be your first port of call. However maybe you can't change Base. In that case, are any of the functions (like the destructor) virtual? If so, we can use dynamic_cast:

void caller(Base &obj)   // must stay as it is
{
    Derived& d_ref = dynamic_cast<Derived&>(obj);
    d_ref.fun();
}

The nice thing about this, is that if obj is not actually a Derived object, the dynamic_cast will throw std::invalid_cast, and you don't get undefined behaviour.

Finally, maybe you can't change Base, and there are no virtual functions. In that case, your only option is to reach for the blunderbuss of static_cast - however, be warned, this is how C++ programmers not merely shoot themselves in the foot, but blow their whole leg off. (It's not quite as bad as the plasma cannon of reintepret_cast, but you really don't want to use it like this unless you have to.)

void caller(Base &obj)   // must stay as it is
{
    Derived& d_ref = static_cast<Derived&>(obj);
    d_ref.fun();
}

One can have to write this sort of thing in production code, but often Base has some sort of "what kind of thing am I" indicator, and you can check that before using the static_cast.

Upvotes: 3

scohe001
scohe001

Reputation: 15446

It is not possible to call a Derived member function on a Base class object.

Every Derived is a Base, but a Base is not a Derived. In other words, Derived may have a whole bunch of information that Base lacks. You may call a Base member function on a Derived class object, but not the other way.

In other words...

class Base {
    public: void b_thing() { }
};

class Derived : public Base {
    public: void d_thing() { }
};

int main() {
    Base b;
    Derived d;
    
    d.b_thing(); // Okay!
    b.d_thing(); // Bad :(
}

However, you can have a Base reference that references a Derived object:

Base &b_ref = d;

And since this is actually a Derived object, you can have it call Derived member functions (that were declared virtual in Base) via polymorphism:

class Base {
    public: virtual int thing() { return 0; }
};

class Derived : public Base {
    public: int thing() { return 1; }
};

int caller(Base &obj)
{
    return obj.thing(); // Returns 1 if obj actually references a Derived object
}

int main() {
    Base b;
    Derived d;
    
    caller(d); // 1
    caller(b); // 0
}

However, notice that when passed a Base object (when obj is actually referencing a Base object), we still call the Base member function.

Upvotes: 4

Related Questions