Jack
Jack

Reputation: 35

Inheritance and pointers in C++

I have this code example and I want to understand why it behaves the way it does. This is a question from a past exam paper in an intro C++ course. I'm studying for the exam now and trying to solidify my understanding of class inheritance.

#include <iostream>
using namespace std;

class Bird {
  public:
    virtual void noise() { cout << "mumble" << endl; }
    void move() { noise(); cout << "fly" << endl; }
};

class Canary: public Bird {
  public:
    void noise() { cout << "chirp" << endl; }
    void move() { noise(); cout << "flap" << endl; }
};

class Tweety: public Canary {
  public:
    void noise() { cout << "tweet" << endl; }
    void move() { noise(); cout << "run" << endl; }
};

int main() {
    Canary *yellow = new Tweety();
    yellow->noise();
    yellow->move();
    return 0;
}

I've run this code, and the output is:

tweet tweet flap

Which means it's calling the Tweety implementation of noise(), but it's calling the Canary implementation of move(). I'm confused about that. I understand the idea of polymorphism, and noise() is virtual, so it makes sense that it calls the Tweety version, since *yellow is a pointer to a Tweety. But why does it call the Canary version of move()?

I think what's confusing me, is the line:

Canary *yellow = new Tweety();

This says that *yellow is a Canary pointer, which points to a Tweety object. I'm sort of ok with that, because I get that pointers to base class can point to derived class. But *yellow points to a Tweety, so why doesn't it use Tweety's move()?

Thanks in advance for any help.

Upvotes: 1

Views: 3880

Answers (3)

Sean
Sean

Reputation: 62472

noise is virtual, so it is dynamically dispatched to the Tweety implementation when you call it.

move in not virtual, so the version to call is decided at compile time based on the type of you are dispatching the call through. Since yellow is a Canary the compiler does resolve what will be called at compile time and will explicitly call the move method in Canary.

Upvotes: 6

Eljay
Eljay

Reputation: 5321

Sean and Alex are spot on.

Here are some more call cases that should help make sense of the different scenarios.

#include <iostream>
using namespace std;

class Bird {
  public:
    virtual void noise() { cout << "mumble" << endl; }
    void move() { noise(); cout << "fly" << endl; }

    void noise2() { cout << "mumble2" << endl; }
    virtual void move2() { noise2(); cout << "fly2" << endl; }
};

class Canary: public Bird {
  public:
    void noise() { cout << "chirp" << endl; }
    void move() { noise(); cout << "flap" << endl; }

    void noise2() { cout << "chirp2" << endl; }
    void move2() { noise2(); cout << "flap2" << endl; }
};

class Tweety: public Canary {
  public:
    void noise() { cout << "tweet" << endl; }
    void move() { noise(); cout << "run" << endl; }

    void noise2() { cout << "tweet2" << endl; }
    void move2() { noise2(); cout << "run2" << endl; }
};

int main() {
    Canary *yellow = new Tweety();
    yellow->noise();
    yellow->move();
    yellow->noise2();
    yellow->move2();
    return 0;
}

/* OUTPUT:
tweet    <- virtual dispatch
tweet    <- virtual dispatch, via Bird::move()
flap     <- direct call
chirp2   <- direct call
tweet2   <- direct call, from Tweety::move2()
run2     <- virtual dispatch
*/

Upvotes: 0

Alex
Alex

Reputation: 10126

The move() should also be virtual otherwise the version of the pointer type is called.

Upvotes: 1

Related Questions