Reputation: 35
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
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
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
Reputation: 10126
The move()
should also be virtual
otherwise the version of the pointer type is called.
Upvotes: 1