Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 65 66 67 68 69 70 71 72 73 ... 170

15:      void Sleep()const { 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 stepn"; }

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. В случае замещения одного из перегруженных методов скрытыми оказываются все варианты этого метода в базовом классе. Если вы хотите использовать в производном классе другие варианты перегруженного метода, то их также нужно заместить в этом классе.

Часто случается ошибка, когда после попытки заместить метод в производном классе данный метод оказывается недоступным для класса из-за того, что программист забыл установить ключевое слово const, используемое при объявлении метода в базовом классе. Вспомните, что слово const является частью сигнатуры, а несоответствие сигнатур ведет к скрытию базового метода, а не к его замещению.

Замещение и сокрытие

В следующем разделе главы будут рассматриваться виртуальные методы. Замещение виртуальных методов ведет к полиморфизму, а сокрытие методов разрушает поли- морфизм. Скоро вы узнаете об этом больше.

Вызов базового метода

Даже если вы заместили базовый метод, то все равно можете обратиться к нему, указав базовый класс, где хранится исходное объявление метода. Для этого в обращении к методу нужно явно указать имя базового класса, за которым следуют два символа двоеточия и имя метода. Например: Mammal: :Move().

Если в листинге 11.6 переписать строку 32 так, как показано ниже, то ошибка во время компиляции больше возникать не будет:

32: fido.Mammal::Move();

Такая запись, реализованная в листинге 11.7, называется явным обрашением к методу базового класса.

Листинг 11.7. Явное обращение к методу базового класса

1: //Листинг 11.7. Явное обращение к методу базового класса

2:

3: #include <iostream.h>

4:

5: class Mammal

6: {

7:    public:

8:       void Move() const { cout << "Mammal move one stepn"; }

9:       void Move(int distance) const

10:      {

11:         cout << "Mammal move " << distance;

12:         cout << " steps.n";

13:      }

14:

15:   protected:

16:      int itsAge;

17:      int itsWeight;

18: };

19:

20: class Dog : public Mammal

21: {

22:    public:

23:       void Move()const;

24:

25: };

26:

27: void Dog::Move() const

28: {

29:    cout << "In dog move...n";

30:    Mammal::Move(3);

31: }

32:

33: int main()

34: {

35:    Mammal bigAnimal;

36:    Dog fido;

37:    bigAnimal.Move(2);

38:    fido.Mammal::Move(6);

39:    return 0;

40: }

Результат:

Mammal move 2 steps.

Mammal move 6 steps.

Анализ: В строке 35 создается объект bigAnimal класса Mammal, а в строке 36 — объект fido класса Dog. В строке 37 вызывается метод Move(int) из базового класса для объекта класса Dog.

В предыдущей версии программы мы столкнулись с проблемой из-за того, что в классе Dog доступен только один замещенный метод Move(), в котором не задаются параметры. Проблема была разрешена явным обращением к методу Move(int) базового класса в строке 38.

Рекомендуется:Повышайте функциональные возможности класса путем создания новых производных классов. Изменяйте выполнение отдельных функций в производных классах с помощью замещения методов.

Не рекомендуется:Не допускайте сокрытие функций базового класса из-за несоответствия сигнатур.

Виртуальные методы

 В этой главе неоднократно подчеркивалось, что объекты класса Dog одновременно являются объектами класса Mammal. До сих пор под этим подразумевалось, что объекты класса Dog наследуют все атрибуты (данные) и возможности (методы) базового класса. Но в языке C++ принципы иерархического построения классов несут в себе еще более глубинный смысл.

Полиморфизм в C++ развит настолько, что допускается присвоение указателям на базовый класс адресов объектов производных классов, как в следующем примере:

Mammal* pMammal = new Dog;

Данное выражение создает в области динамической памяти новый объект класса Dog и возвращает указатель на этот объект, который является указателем класса Mammal. Это вполне логично, так как собака — представитель млекопитающих.

1 ... 65 66 67 68 69 70 71 72 73 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий