3

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;
}
Stals
  • 1,543
  • 4
  • 27
  • 52
Jimm
  • 8,165
  • 16
  • 69
  • 118
  • You aren't passing `b1` as an argument, you are passing a copy of `b1` because you are passing by value. `b1` is a `B` and the argument *must be* the value of an `A` because that's what the function specification says. – David Schwartz Oct 12 '12 at 13:47

2 Answers2

6
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.

Community
  • 1
  • 1
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
3

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
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Why does it work on reference and not value? Whether reference or pointer or value, they should have same vtable and hence resolve to correct function? – Jimm Oct 12 '12 at 13:47
  • 1
    @Jimm I just added an explanation with a link to more information. Also, which book? See [The Definitive C++ Book Guide and List](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) for good suggestions – sehe Oct 12 '12 at 13:52
  • +1 for adding a link to http://liveworkspace.org. I have been searching for such a site for c++, but only found sites that supported dynamic languages. Ty!! – Jimm Oct 12 '12 at 13:56