pythonic
pythonic

Reputation: 21599

Strange behavior with virtual functions

With the following code, I would expect output to be B.f B.f DD.f, but instead the output I get is B.f B.f B.f. How is that possible, when DD derives from D which has f as virtual.

class B
{
public:
    void f() { cout << "B.f "; }
};

class D : public B
{
public:
    virtual void f() { cout << "D.f "; }
};

class DD : public D{
public:
    virtual void f() { cout << "DD.f "; }
};

B * b = new B();
B * d = new D();
B * dd = new DD();

b->f();
d->f();
dd->f();

Upvotes: 1

Views: 88

Answers (5)

Jean-Baptiste Yun&#232;s
Jean-Baptiste Yun&#232;s

Reputation: 36391

When you use a reference or a pointer to call a method, the compiler searches in the type of the pointer or reference at the declaration of the method (here it searches in B the declaration of some method with signature f()). When it finds one :

  • if it is NOT marked as virtual, then it solves it as a call to the method defined for this class - this is a static binding.
  • if it is marked as virtual, then the method called will be the appropriate one of the object referenced or pointed by - this is a dynamic binding.

The next test would have been :

DD * dd = new DD();
D * d = dd;
B * b = d;

b->f();
d->f();
dd->f();

One single object new DD() that is used/viewed differently... Each type can be thought as a kind of view you have on an object. If you see it as a B then f() does something but always the same thing, but if you see it as D or DD, f() does something different...

If you meet someone in the street, the standard way for him to salute you is to say hello, but for the same person, when he meets a friend of him he can either say hi! or Yo! :

class Person {
public:
  void salute() { cout << "Hello" << endl; }
};
class Friend : public Person {
public:
  virtual void salute() { cout << "Hi!" << endl; }
};
class RoomMate : public Friend {
public:
  virtual void salute() { cout << "Yo!" << endl; }
};
void asACustomer(Person &p) {
   p.salute(); // static binding, we need the standard politeness
}
void asAFriend(Friend &f) {
    p.salute(); // dynamic binding, we want an appropriate message...
}
RoomMate joe;
asCustomer(joe);
asFriend(joe);

With static binding you know at compile time which method is called; with dynamic binding you cannot, you only know that an appropriate one will be. This is a key point in sub-typing polymorphism.

In general, be careful when mixing static and dynamic binding for a method.

Upvotes: 0

Subhajit
Subhajit

Reputation: 320

As soon as you declare f() virtual in B,this starts to maintain a virtual table that holds the function pointers of all the other functions of same name of derived classes. This is a lookup table that is used to resolve function calls in a dynamic/late binding manner.

Upvotes: 0

billz
billz

Reputation: 45410

You need to set B::f() to virtual, without set B::f() to virtual, it won't appear in B virtual table(vtbl), thus B::f() is called instead of dispatch the call to derived class.

class B
{
public:
   virtual void f() { cout << "B.f "; }
};

Upvotes: 1

Luchian Grigore
Luchian Grigore

Reputation: 258548

Functions become virtual from the level they were declared virtual up. You first declare f virtual in D, which means the dynamic dispatch will only happen from D upwards. B doesn't, nor should it know about the deriving classes.

Think about how the compiler sees it:

You have a pointer to B - B has the following definition:

class B
{
public:
    void f() { cout << "B.f "; }
};

Since f is not virtual, I'll just go ahead and resolve the call statically - i.e. B::f().

Upvotes: 4

juanchopanza
juanchopanza

Reputation: 227370

For dynamic dispatch to work from a pointer to B, you need to make f() virtual in B:

class B
{
public:
    virtual void f() { cout << "B.f "; }
};

Upvotes: 2

Related Questions