Implementing Inheritance 393
12
On line 29, a pointer to Mammalis created (pDog), but it is assigned the address of a new
Dogobject. Because a dog is a mammal, this is a legal assignment. The pointer is then
used on line 30 to call the Move()function. Because the compiler knows pDogonly to be
a Mammal, it looks to the Mammalobject to find the Move()method. On line 10, you can
see that this is a standard, nonvirtual method, so the Mammal’s version is called.
On line 31, the pointer then calls the Speak()method. Because Speak()is virtual (see
line 11), the Speak()method overridden in Dogis invoked.
This is almost magical. As far as the calling function knew, it had a Mammalpointer, but
here a method on Dogwas called. In fact, if you had an array of pointers to Mammal, each
of which pointed to a different subclass of Mammal, you could call each in turn, and the
correct function would be called. Listing 12.9 illustrates this idea.
LISTING12.9 Multiple Virtual Functions Called in Turn
1: //Listing 12.9 Multiple virtual functions called in turn
2: #include <iostream>
3: using namespace std;
4:
5: class Mammal
6: {
7: public:
8: Mammal():itsAge(1) { }
9: virtual ~Mammal() { }
10: virtual void Speak() const { cout << “Mammal speak!\n”; }
11:
12: protected:
13: int itsAge;
14: };
15:
16: class Dog : public Mammal
17: {
18: public:
19: void Speak()const { cout << “Woof!\n”; }
20: };
21:
22: class Cat : public Mammal
23: {
24: public:
25: void Speak()const { cout << “Meow!\n”; }
26: };
27:
28:
29: class Horse : public Mammal
30: {
31: public:
32: void Speak()const { cout << “Winnie!\n”; }