Reputation: 1607
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
Reputation: 28987
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
Reputation: 15446
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