Освой самостоятельно С++ за 21 день.
Шрифт:
17: class Dog : public Mammal
18: {
19: public:
20: Dog { cout << "Dog Constructor...\n"; }
21: virtual ~Dog { cout << "Dog destructor...\n"; }
22: void WagTail { cout << "Wagging Tail...\n"; }
23: void Speakconst { cout << "Woof!\n"; }
24: void Moveconst { cout << "Dog moves 5 steps...\n"; }
25: };
26:
27: int main
28: {
29:
30: Mammal *pDog = new Dog;
31: pDog->Move;
32: pDog->Speak;
33:
34: return 0;
35: }
Результат:
Mammal constructor...
Dog Constructor...
Mammal move one step
Woof!
Анализ:
В строке 30 создается указатель класса Mammal (pDog), но ему присваивается адрес нового объекта производного класса Dog. Поскольку собака является млекопитающим, это вполне логично. Данный указатель затем используется для вызова функции Move. Поскольку pDog известен компилятору как указатель класса Mammal, результат получается таким же, как при обычном вызове метода Move из объекта класса Mammal.
В строке 32 через указатель pDog делается обращение к методу Speak. В данном случае метод Speak объявлен как виртуальный, поэтому вызывается вариант функции Speak, замещенный в классе Dog.
Это кажется каким-то волшебством. Хотя компилятор знает, что указатель pDog принадлежит классу Mammal, тем не менее происходит вызов версии функции, объявленной в другом производном классе. Если создать массив указателей базового класса, каждый из которых указывал бы на объект своего производного класса, то, обращаясь попеременно к указателям данного массива, можно управлять выполнением всех вариантов замещенного метода. Эта идея реализована в листинге 11.9.
Листинг 11.9. Произвольное обращение к набору виртуальных функций
1: //Листинг 11.9. Произвольное обращение к набору виртуальных функций
2:
3: #include <iostream.h>
4:
5: class Mammal
6: {
7: public:
8: Mammal:itsAge(1) { }
9: virtual ~Mammal { }
10: virtual void Speak const { cout << "Mammal speak!\n"; }
11: protected:
12: int itsAge;
13: };
14:
15: class Dog : public Mammal
16: {
17: public:
18: void Speakconst { cout << "Woof!\n"; }
19: };
20:
21:
22: class Cat : public Mammal
23: {
24: public:
25: void Speakconst { cout << "Meow!\n"; }
26: };
27:
28:
29: class Horse : public Mammal
30: {
31: public:
32: void Speakconst { cout << "Whinny!\n"; }
33: };
34:
35: class Pig : public Mammal
36: {
37: public:
38: void Speakconst < cout << "Oink!\n"; }
39: };
40:
41: int main
42: {
43: Mammal* theArray[5];
44: Mammal* ptr;
45: int choice, i;
46: for ( i = 0; i<5; i++)
47: {
48: cout << "(1)dog (2)cat (3)horse (4)pig: ";
49: cin >> choice;
50: switch (choice)
51: {
52: case 1: ptr = new Dog;
53: break;
54: case 2; ptr = new Cat;
55: break;
56: case 3: ptr = new Horse;
57: break;
58: case 4: ptr = new Pig;
59: break;
60: default: ptr = new Mammal;
61: break;
62: }
63: theArray[i] = ptr;
64: }
65: for (i=0;i<5;i++)
66: theArray[i]->Speak;
67: return 0;
68: }
Результат:
(1)dog (2)cat (3)horse (4)pig: 1
(1)dog (2)cat (3)horse (4)pig: 2
(1)dog (2)cat (3)horse (4)pig: 3
(1)dog (2)cat (3)horse (4)pig; 4
(1)dog (2)cat (3)horse (4)pjg: 5
Woof!
Meow!
Whinny!
0ink!
Mammal speak!
Анализ: Чтобы идея использования виртуальных функций была понятнее, в данной программе этот метод раскрыт наиболее явно и четко. Сначала определяется четыре класса — Dog, Cat, Horse и Pig, которые являются производными от базового класса Mammal.
В строке 10 объявляется виртуальная функция Speak класса Mammal. В строках 18, 25, 32 и 38 указанная функция замещается во всех соответствующих производных классах.
Пользователю предоставляется возможность выбрать объект любого производного класса, и в строках 46—64 создается и добавляется в массив указатель класса Mammal на вновь созданный объект.
Вопросы и ответы
Если функция-член была объявлена как виртуальная в базовом классе, следует ли повторно указывать виртуальность при объявлении этого метода в произ- водном классе?
Нет. Если метод уже был объявлен как виртуальный, то он будет оставаться таким, несмотря на замещение его в производном классе. В то же время для повышения читабельности программы имеет смысл (но не требуется) и в производных классах продолжать указывать на виртуальность данного метода с помощью ключевого слова virtual.