Освой самостоятельно С++ за 21 день.
Шрифт:
12:
13: //Другие методы
14: void Speakconst { cout << "Mammal sound!\n"; }
15: void Sleepconst { cout << "shhh. I'm sleeping.\n"; }
16:
17:
18: protected:
19: int itsAge;
20: int itsWeight;
21: };
22:
23: class Dog : public Mammal
24: {
25: public:
26:
27: // Конструкторы
28: Dog{ cout << "Dog constructor...\n"; }
29: ~Dog{ cout << "Dog destructor...\n"; }
30:
31: //
32: void WagTail const { cout << "Tail wagging...\n"; }
33: void BegForFood const { cout << "Begging for food...\n"; }
34: void Speak const { cout << "Woof!\n"; }
35:
36: private:
37: BREED itsBreed;
38: };
39:
40: int main
41: {
42: Mammal bigAnimal;
43: Dog fido;
44: bigAnimal.Speak;
45: fido.Speak;
46: return 0;
47: }
Результат:
Mammal constructor...
Mammal constructor...
Dog constructor...
Mammal sound!
Woof!
Dog destructor...
Mammal destructor...
Mammal destructor...
Анализ: В строке 34 в классе Dog происходит замещение метода базового класса
Speak, в результате чего в случае вызова этой функции объектом класса Dog на экран выводится Woof!. В строке 42 создается объект bigAnimal класса Mammal, в результате чего вызывается конструктор класса Mammal и на экране появляется первая строка. В строке 43 создается объект Fido класса Dog, что сопровождается последовательным вызовом сначала конструктора класса Mammal, а затем конструктора класса Dog. Соответственно на экран выводится еще две строки.
В строке 44 объект класса Mammal вызывает метод Speak, а в строке 45 уже объект класса Dog обращается к этому методу. На экран при этом выводится разная информация, так как метод Speak в классе Dog замещен. Наконец выполнение программы выходит за область видимости объектов и для их удаления вызываются соответствующие пары деструкторов.
Перегрузка или замещение
Эти схожиеподходы приводят почти к одинаковым результатам. При перегрузке метода создается несколько вариантов этогометода с одним и тем же именем, но с разными сигнатурами. При замещении в производном классе используется метод с тем же именем и сигнатурой, что и в базовом классе, но с изменениями в теле функции.
Сокрытие метода базового класса
В предыдущем примере при обращении к методу Speak из объекта класса Dog программа выполнялась не так, как было указано при объявлении метода Speak в базовом классе. Казалось бы, это то, что нам нужно. Если в классе Mammal есть некоторый метод Move, который замещается в классе Dog, то можно сказать, что метод Move класса Dog скрывает метод с тем же именем в базовом классе. Однако в некоторых случаях результат может оказаться неожиданным.
Усложним ситуацию. Предположим, что в классе Mammal метод Move трижды перегружен. В одном варианте метод не требует параметров, в другом используется один целочисленный параметр (дистанция), а в третьем — два целочисленных параметра (скорость и дистанция). В классе Dog замещен метод Move без параметров. Тем не менее попытка обратиться из объекта класса Dog к двум другим вариантам перегруженного метода класса Mammal окажется неудачной. Суть проблемы раскрывается в листинге 11.6.
Листинг 11.6. Сокрытие методов
1: //Листинг 11.6. Сокрытие методов
2:
3: #include <iostream.h>
4:
5: class Mammal
6: {
7: public:
8: void Move const { cout << "Mammal move one step\n"; }
9: void Move(int distance) const
10: {
11: cout << "Mammal move ";
12: cout << distance <<" steps.\n";
13: }
14: protected:
15: int itsAge;
16: int itsWeight;
17: };
18:
19: class Dog : public Mammal
20: {
21: public:
22: // Возможно, последует сообщение, что функция скрыта!
23: void Move const { cout << "Dog move 5 steps.\n"; }
24: };
25:
26: int main
27: {
28: Mammal bigAnimal;
29: Dog fido;
30: bigAnimal.Move;
31: bigAnimal.Move(2);
32: fido.Move;
33: // fido.Move(10);
34: return 0;
35: }
Результат:
Mammal move one step
Mammal move 2 steps.
Dog move 5 steps.
Анализ: В данном примере из программы были удалены все другие методы и данные, рассмотренные нами ранее. В строках 8 и 9 в объявлении класса Mammal перегружаются методы Move. В строке 23 происходит замещение метода Move без параметров в классе Dog. Данный метод вызывается для объектов разных классов в строках 30 и 32, и информация, выводимая на экран, подтверждает, что замещение метода прошло правильно.
Однако строка 33 заблокирована, так как она вызовет ошибку компиляции. Хотя логично было предположить, что в классе Dog свободно можно использовать метод Move(int), поскольку замещен был только метод Move, но в действительности в данной ситуации, чтобы использовать Move(int), его также нужно заместить в классе Dog. В случае замещения одного из перегруженных методов скрытыми оказываются все варианты этого метода в базовом классе. Если вы хотите использовать в производном классе другие варианты перегруженного метода, то их также нужно заместить в этом классе.