Jimm
Jimm

Reputation: 8505

c++ virtual function confusion

I am learning about virtual functions and i am very confused with the results of the below program I was hoping that both a1.virFun(b1) and b1.virFun(b1) should return "hello from B", but the program returns "hello from A". This is contrary to my understanding. Can you please explain why b1.sayHello() is not being invoked, even when i am passing b1 as argument and b1.sayHello() is virtual function.

#include<iostream>

using namespace std;

class A
{
 public:
 virtual void sayHello();
 void virFun(A obj);

 };

class B : public A
{
 public:
 void virFun(A obj);
 virtual void sayHello();

};

void A::sayHello()
 {
   cout << "hello from A" << endl;
 }

 void B::sayHello()
 {
   cout <<"hello from B" << endl;
 }

 void A::virFun(A obj)
 {
    obj.sayHello();
 }

 void B::virFun(A obj)
{
    obj.sayHello();
}

int main()
{
 A a1;
 B b1;

a1.virFun(b1);
b1.virFun(b1);

return 0;
}

Upvotes: 3

Views: 170

Answers (2)

John Kugelman
John Kugelman

Reputation: 361555

void virFun(A obj);

For virtual functions to work you need to pass objects by reference or by pointer to ensure that you're always working with the original object.

void virFun(const A &obj);
void virFun(const A *obj);

Otherwise virFun() will receive a copy of your object, which ends up "slicing" it and losing the derived class's information. The copy has A's vtable and is missing B's extra fields. This is usually a disaster.

As a general rule, pass objects around using T& or const T& rather than plain T. Aside from the slicing problem, it's also more efficient.

Upvotes: 6

sehe
sehe

Reputation: 392833

Make virFun take a reference, not an A object by value:

 void A::virFun(A& obj) { obj.sayHello(); }

Reason: If you take an A by value, the parameter will be initialized by making a copy of the value passed. If you pass a B, it will copy the 'A' part of it into a new variable of the parameter type (A) - this is known as object slicing. The parameter will no longer 'be-a' B instance, so it will behave as an A instance, alright.

No need to override virFun in class B, since it uses the argument obj in both cases

In fact, virFun could be a static function. The name is confusing since it isn't even a virFun (virtual function) - it is a regular function that uses a virtual function.

#include<iostream>

using namespace std;

struct A     { virtual     void sayHello() { cout << "hello from A" << endl; } };
struct B : A { /*virtual*/ void sayHello() { cout << "hello from B" << endl; } };

static void virFun(A& obj)
{
    obj.sayHello();
}

int main()
{
    A a1;
    B b1;

    virFun(a1);
    virFun(b1);
}

See it live on http://liveworkspace.org/code/1624496ced29eb4683f5b19072f72f60

hello from A
hello from B

Upvotes: 3

Related Questions