1

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.

5
  • This example has no multiple inheritance. To unserstand the behaviour you need to understand the difference between virtual and non-virtual member functions. Commented Nov 8, 2017 at 16:19
  • You are calling move on a Canary pointer and move is not virtual on a Canary. Commented Nov 8, 2017 at 16:19
  • 1
    I did not know birds "mumble" Commented Nov 8, 2017 at 16:22
  • 1
    A good C++ book should explain that. Get one! Commented Nov 8, 2017 at 17:03
  • No body see memory leak ? Commented May 2, 2022 at 13:18

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

3 Comments

+1. Minor thing: I'd replace "the compiler can resolve" with "the compiler does resolve" - since this is not optional, butrequired behavior.
Follow-up/clarify question: if yellow is a Canary, then why doesn't the virtual function move call the Canary implementation of move? You're saying that the pointer type determines the implementation for non-virtual functions, but what the pointer points to determines it for virtual functions? And that's because of how virtual works with the compiler, i.e. dynamic dispatch as you called it?
@Jack - yellow has a static type of Canary. Because you're calling via a pointer then polymorphism will apply to any virtual function you call. noise is a virtual function so it resolves to the implementation in Tweety as that's what yellow points to. move isn't virtual so it's resolved statically. This means the compiler looks at the static type of yellow (a Canary) and calls the move function in Canary. If you want it to be dynamically resolved at runtime you'd have to make move virtual.
1

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

Comments

0

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
*/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.